From d926610bcff322fb38411c4009577da4cde59cc4 Mon Sep 17 00:00:00 2001 From: Judit Varsanyi Rozsa Date: Mon, 25 Mar 2019 14:35:14 -0700 Subject: [PATCH 01/10] port ControlDesigner --- src/Common/src/NativeMethods.cs | 85 +- src/Common/src/UnsafeNativeMethods.cs | 7 +- .../src/Resources/SR.Designer.cs | 1160 ++++++++ .../src/Resources/SR.resx | 189 +- .../src/Resources/xlf/SR.cs.xlf | 20 + .../src/Resources/xlf/SR.de.xlf | 20 + .../src/Resources/xlf/SR.es.xlf | 20 + .../src/Resources/xlf/SR.fr.xlf | 20 + .../src/Resources/xlf/SR.it.xlf | 20 + .../src/Resources/xlf/SR.ja.xlf | 20 + .../src/Resources/xlf/SR.ko.xlf | 20 + .../src/Resources/xlf/SR.pl.xlf | 20 + .../src/Resources/xlf/SR.pt-BR.xlf | 20 + .../src/Resources/xlf/SR.ru.xlf | 20 + .../src/Resources/xlf/SR.tr.xlf | 20 + .../src/Resources/xlf/SR.zh-Hans.xlf | 20 + .../src/Resources/xlf/SR.zh-Hant.xlf | 20 + .../Forms/Design/Behavior/BehaviorService.cs | 1650 ++++++++++- .../Behavior/ContainerSelectorBehavior.cs | 247 ++ .../Design/Behavior/ContainerSelectorGlyph.cs | 75 + .../Forms/Design/Behavior/ResizeBehavior.cs | 899 ++++++ .../Windows/Forms/Design/ControlDesigner.cs | 2528 +++++++++++++++-- 22 files changed, 6761 insertions(+), 339 deletions(-) create mode 100644 src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorBehavior.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorGlyph.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ResizeBehavior.cs diff --git a/src/Common/src/NativeMethods.cs b/src/Common/src/NativeMethods.cs index 10eb0da4654..5418e1a9156 100644 --- a/src/Common/src/NativeMethods.cs +++ b/src/Common/src/NativeMethods.cs @@ -55,7 +55,8 @@ namespace System.Windows.Forms { using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.Versioning; - + using static System.Windows.Forms.NativeMethods; + /// internal static class NativeMethods { @@ -975,6 +976,12 @@ public const int public const int LOCALE_IMEASURE = 0x0000000D; // 0 = metric, 1 = US + public const int TVM_SETEXTENDEDSTYLE = TV_FIRST + 44; + public const int TVM_GETEXTENDEDSTYLE = TV_FIRST + 45; + + public const int TVS_EX_FADEINOUTEXPANDOS = 0x0040; + public const int TVS_EX_DOUBLEBUFFER = 0x0004; + public static readonly int LOCALE_USER_DEFAULT = MAKELCID(LANG_USER_DEFAULT); public static readonly int LANG_USER_DEFAULT = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); @@ -4793,7 +4800,8 @@ public class NMHEADER { public int iButton = 0; public IntPtr pItem = IntPtr.Zero; // HDITEM* } - + + [SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] [StructLayout(LayoutKind.Sequential)] public class MOUSEHOOKSTRUCT { // pt was a by-value POINT structure @@ -6009,6 +6017,79 @@ public UiaRect(System.Drawing.Rectangle r) { // This value requires KB2533623 to be installed. // Windows Server 2003 and Windows XP: This value is not supported. internal const int LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, [In, Out] ref RECT rect, int cPoints); + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, [In, Out] POINT pt, int cPoints); + + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr WindowFromPoint(int x, int y); + [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); + //[DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + //[ResourceExposure(ResourceScope.None)] + //public extern static IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, [In, Out] HDHITTESTINFO lParam); + [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam); + [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, string lParam); + [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, [In, Out] TV_HITTESTINFO lParam); + + [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern short GetKeyState(int keyCode); + [DllImport(ExternDll.Gdi32, ExactSpelling = true, EntryPoint = "DeleteObject", CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern bool IntDeleteObject(IntPtr hObject); + public static bool DeleteObject(IntPtr hObject) + { + System.Internal.HandleCollector.Remove(hObject, CommonHandles.GDI); + return IntDeleteObject(hObject); + } + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetUpdateRect(IntPtr hwnd, [In, Out] ref RECT rc, bool fErase); + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetUpdateRgn(IntPtr hwnd, IntPtr hrgn, bool fErase); + + [DllImport(ExternDll.Gdi32, ExactSpelling = true, EntryPoint = "CreateRectRgn", CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntCreateRectRgn(int x1, int y1, int x2, int y2); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreateRectRgn(int x1, int y1, int x2, int y2) + { + return System.Internal.HandleCollector.Add(IntCreateRectRgn(x1, y1, x2, y2), CommonHandles.GDI); + } + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetCursor(); + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetCursorPos([In, Out] POINT pt); + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent); } } diff --git a/src/Common/src/UnsafeNativeMethods.cs b/src/Common/src/UnsafeNativeMethods.cs index 47d82801884..8cad0eec50d 100644 --- a/src/Common/src/UnsafeNativeMethods.cs +++ b/src/Common/src/UnsafeNativeMethods.cs @@ -828,7 +828,12 @@ public static int SetWindowRgn(HandleRef hwnd, HandleRef hrgn, bool fRedraw) { [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.LVBKIMAGE lParam); - + + [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(IntPtr hwnd, int msg, bool wparam, int lparam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] public static extern int SendMessage(HandleRef hWnd, int msg, int wParam, ref NativeMethods.LVHITTESTINFO lParam); diff --git a/src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs b/src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs new file mode 100644 index 00000000000..5d427f81a8a --- /dev/null +++ b/src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs @@ -0,0 +1,1160 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Design.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class SR { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal SR() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Windows.Forms.Design.Resources.SR", typeof(SR).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Copy and move {0}. + /// + public static string BehaviorServiceCopyControl { + get { + return ResourceManager.GetString("BehaviorServiceCopyControl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy and move {0} Controls. + /// + public static string BehaviorServiceCopyControls { + get { + return ResourceManager.GetString("BehaviorServiceCopyControls", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Move {0}. + /// + public static string BehaviorServiceMoveControl { + get { + return ResourceManager.GetString("BehaviorServiceMoveControl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Move {0} Controls. + /// + public static string BehaviorServiceMoveControls { + get { + return ResourceManager.GetString("BehaviorServiceMoveControls", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Double cannot be converted to a date.. + /// + public static string CannotConvertDoubleToDate { + get { + return ResourceManager.GetString("CannotConvertDoubleToDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Integer cannot be converted to a float.. + /// + public static string CannotConvertIntToFloat { + get { + return ResourceManager.GetString("CannotConvertIntToFloat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unhandled VT: {0}.. + /// + public static string COM2UnhandledVT { + get { + return ResourceManager.GetString("COM2UnhandledVT", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Format {0} components (alignment). + /// + public static string CommandSetAlignByPrimary { + get { + return ResourceManager.GetString("CommandSetAlignByPrimary", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Align {0} components to grid. + /// + public static string CommandSetAlignToGrid { + get { + return ResourceManager.GetString("CommandSetAlignToGrid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cut {0} Components. + /// + public static string CommandSetCutMultiple { + get { + return ResourceManager.GetString("CommandSetCutMultiple", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete {0} components. + /// + public static string CommandSetDelete + { + get + { + return ResourceManager.GetString("CommandSetDelete", resourceCulture); + } + } + /// Looks up a localized string similar to The serialization store is closed. New objects cannot be added to a closed store.. + /// + public static string CodeDomComponentSerializationServiceClosedStore { + get { + return ResourceManager.GetString("CodeDomComponentSerializationServiceClosedStore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to An error occurred while processing this command.\r\n{0}. + /// + public static string CommandSetError + { + get + { + return ResourceManager.GetString("CommandSetError", resourceCulture); + } + } + /// Looks up a localized string similar to Complete deserialization of {0} failed.. + /// + public static string CodeDomComponentSerializationServiceDeserializationError { + get { + return ResourceManager.GetString("CodeDomComponentSerializationServiceDeserializationError", resourceCulture); + + } + } + + /// + /// Looks up a localized string similar to Format {0} components (spacing). + /// + public static string CommandSetFormatSpacing { + get { + return ResourceManager.GetString("CommandSetFormatSpacing", resourceCulture); + } + } + /// Looks up a localized string similar to This type of serialization store is not supported. Use a store returned by the CreateStore method.. + /// + public static string CodeDomComponentSerializationServiceUnknownStore { + get { + return ResourceManager.GetString("CodeDomComponentSerializationServiceUnknownStore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Paste components. + /// + public static string CommandSetPaste { + get { + return ResourceManager.GetString("CommandSetPaste", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size {0} components. + /// + public static string CommandSetSize { + get { + return ResourceManager.GetString("CommandSetSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size {0} components to grid. + /// + public static string CommandSetSizeToGrid { + get { + return ResourceManager.GetString("CommandSetSizeToGrid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unknown spacing command. + /// + public static string CommandSetUnknownSpacingCommand + { + get + { + return ResourceManager.GetString("CommandSetUnknownSpacingCommand", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Adding event '{0}'. + /// + public static string ComponentDesignerAddEvent { + get { + return ResourceManager.GetString("ComponentDesignerAddEvent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not convert value '{0}' to the type '{1}'.. + /// + public static string DesignerActionPanel_CouldNotConvertValue { + get { + return ResourceManager.GetString("DesignerActionPanel_CouldNotConvertValue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not find method '{0}'. + /// + public static string DesignerActionPanel_CouldNotFindMethod { + get { + return ResourceManager.GetString("DesignerActionPanel_CouldNotFindMethod", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not find property '{0}' on '{1}'.. + /// + public static string DesignerActionPanel_CouldNotFindProperty { + get { + return ResourceManager.GetString("DesignerActionPanel_CouldNotFindProperty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} Tasks. + /// + public static string DesignerActionPanel_DefaultPanelTitle { + get { + return ResourceManager.GetString("DesignerActionPanel_DefaultPanelTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error using the dropdown: {0}. + /// + public static string DesignerActionPanel_ErrorActivatingDropDown { + get { + return ResourceManager.GetString("DesignerActionPanel_ErrorActivatingDropDown", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error invoking '{0}'. Details: {1}. + /// + public static string DesignerActionPanel_ErrorInvokingAction { + get { + return ResourceManager.GetString("DesignerActionPanel_ErrorInvokingAction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error setting value '{0}' to property '{1}'. Details: {2}. + /// + public static string DesignerActionPanel_ErrorSettingValue { + get { + return ResourceManager.GetString("DesignerActionPanel_ErrorSettingValue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Call to BeginDrag must succeed before calling drag functions.. + /// + public static string DesignerBeginDragNotCalled { + get { + return ResourceManager.GetString("DesignerBeginDragNotCalled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot remove or destroy inherited component '{0}'.. + /// + public static string DesignerHostCantDestroyInheritedComponent { + get { + return ResourceManager.GetString("DesignerHostCantDestroyInheritedComponent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot add an instance of {0} to the designer because it would create a circular dependency. Make sure that the type does not have the same namespace and type name as the root component {1}.. + /// + public static string DesignerHostCyclicAdd { + get { + return ResourceManager.GetString("DesignerHostCyclicAdd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failure initializing the designer. It has no Component member.. + /// + public static string DesignerHostDesignerNeedsComponent { + get { + return ResourceManager.GetString("DesignerHostDesignerNeedsComponent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting '{0}'.. + /// + public static string DesignerHostDestroyComponentTransaction { + get { + return ResourceManager.GetString("DesignerHostDestroyComponentTransaction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The name {0} is already in use by another component.. + /// + public static string DesignerHostDuplicateName { + get { + return ResourceManager.GetString("DesignerHostDuplicateName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Component of type {0} could not be created. Make sure the type implements IComponent and provides an appropriate public constructor. Appropriate constructors either take no parameters or take a single IContainer parameter.. + /// + public static string DesignerHostFailedComponentCreate { + get { + return ResourceManager.GetString("DesignerHostFailedComponentCreate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No Description Available. + /// + public static string DesignerHostGenericTransactionName { + get { + return ResourceManager.GetString("DesignerHostGenericTransactionName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A designer loader has already been configured for this design surface.. + /// + public static string DesignerHostLoaderSpecified { + get { + return ResourceManager.GetString("DesignerHostLoaderSpecified", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The designer transaction '{0}' cannot be committed or canceled because nested transaction '{1}' is still active. Commit or cancel the nested transaction first.. + /// + public static string DesignerHostNestedTransaction { + get { + return ResourceManager.GetString("DesignerHostNestedTransaction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot open a designer for the file because the class within it does not inherit from a class that can be visually designed.. + /// + public static string DesignerHostNoBaseClass { + get { + return ResourceManager.GetString("DesignerHostNoBaseClass", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There is no designer for the class {0}.. + /// + public static string DesignerHostNoTopLevelDesigner { + get { + return ResourceManager.GetString("DesignerHostNoTopLevelDesigner", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New components cannot be added while a designer is unloading.. + /// + public static string DesignerHostUnloading { + get { + return ResourceManager.GetString("DesignerHostUnloading", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Inherited control. + /// + public static string DesignerInherited { + get { + return ResourceManager.GetString("DesignerInherited", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Inherited control (Private). + /// + public static string DesignerInheritedReadOnly { + get { + return ResourceManager.GetString("DesignerInheritedReadOnly", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Optimized Code Generation. + /// + public static string DesignerOptions_CodeGenDisplay { + get { + return ResourceManager.GetString("DesignerOptions_CodeGenDisplay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Code Generation Settings. + /// + public static string DesignerOptions_CodeGenSettings { + get { + return ResourceManager.GetString("DesignerOptions_CodeGenSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to InSitu Editing. + /// + public static string DesignerOptions_EnableInSituEditingCat { + get { + return ResourceManager.GetString("DesignerOptions_EnableInSituEditingCat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Controls whether InSitu editing is enabled.. + /// + public static string DesignerOptions_EnableInSituEditingDesc { + get { + return ResourceManager.GetString("DesignerOptions_EnableInSituEditingDesc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable InSitu Editing. + /// + public static string DesignerOptions_EnableInSituEditingDisplay { + get { + return ResourceManager.GetString("DesignerOptions_EnableInSituEditingDisplay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sets the default X and Y grid setting on designers when LayoutMode = SnapToGrid.. + /// + public static string DesignerOptions_GridSizeDesc { + get { + return ResourceManager.GetString("DesignerOptions_GridSizeDesc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Default Grid Cell Size. + /// + public static string DesignerOptions_GridSizeDisplayName { + get { + return ResourceManager.GetString("DesignerOptions_GridSizeDisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Layout Settings. + /// + public static string DesignerOptions_LayoutSettings { + get { + return ResourceManager.GetString("DesignerOptions_LayoutSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Controls whether designer smart tag windows should be shown by default.. + /// + public static string DesignerOptions_ObjectBoundSmartTagAutoShow { + get { + return ResourceManager.GetString("DesignerOptions_ObjectBoundSmartTagAutoShow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically Open Smart Tags. + /// + public static string DesignerOptions_ObjectBoundSmartTagAutoShowDisplayName { + get { + return ResourceManager.GetString("DesignerOptions_ObjectBoundSmartTagAutoShowDisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Object Bound Smart Tag Settings. + /// + public static string DesignerOptions_ObjectBoundSmartTagSettings { + get { + return ResourceManager.GetString("DesignerOptions_ObjectBoundSmartTagSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled optimized code generation. Some controls may not be compatible with this mode. For this change to take effect, Visual Studio must be closed and re-opened.. + /// + public static string DesignerOptions_OptimizedCodeGen { + get { + return ResourceManager.GetString("DesignerOptions_OptimizedCodeGen", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Controls whether designers should display a sizing grid when LayoutMode = SnapToGrid.. + /// + public static string DesignerOptions_ShowGridDesc { + get { + return ResourceManager.GetString("DesignerOptions_ShowGridDesc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show Grid. + /// + public static string DesignerOptions_ShowGridDisplayName { + get { + return ResourceManager.GetString("DesignerOptions_ShowGridDisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Controls whether designers should snap to grid dots when LayoutMode = SnapToGrid.. + /// + public static string DesignerOptions_SnapToGridDesc { + get { + return ResourceManager.GetString("DesignerOptions_SnapToGridDesc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Snap to Grid. + /// + public static string DesignerOptions_SnapToGridDisplayName { + get { + return ResourceManager.GetString("DesignerOptions_SnapToGridDisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Controls whether designers should show smart tag popup windows.. + /// + public static string DesignerOptions_UseSmartTags { + get { + return ResourceManager.GetString("DesignerOptions_UseSmartTags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Controls whether designers should use snap lines. If true, snap lines will be used as guides. If false, grid lines will be used.. + /// + public static string DesignerOptions_UseSnapLines { + get { + return ResourceManager.GetString("DesignerOptions_UseSnapLines", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The container cannot be disposed at design time.. + /// + public static string DesignSurfaceContainerDispose { + get { + return ResourceManager.GetString("DesignSurfaceContainerDispose", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot create a view for this design surface because the designer is not loaded.. + /// + public static string DesignSurfaceDesignerNotLoaded { + get { + return ResourceManager.GetString("DesignSurfaceDesignerNotLoaded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0}. + /// + public static string DesignSurfaceFatalError { + get { + return ResourceManager.GetString("DesignSurfaceFatalError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The designer loader did not provide a root component but has not indicated why.. + /// + public static string DesignSurfaceNoRootComponent { + get { + return ResourceManager.GetString("DesignSurfaceNoRootComponent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The designer loaded, but it does not offer a view compatible with this design surface.. + /// + public static string DesignSurfaceNoSupportedTechnology { + get { + return ResourceManager.GetString("DesignSurfaceNoSupportedTechnology", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The service {0} cannot be removed from the service container.. + /// + public static string DesignSurfaceServiceIsFixed { + get { + return ResourceManager.GetString("DesignSurfaceServiceIsFixed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to .NET Component. + /// + public static string DotNET_ComponentType { + get { + return ResourceManager.GetString("DotNET_ComponentType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Drag {0} components. + /// + public static string DragDropDragComponents + { + get + { + return ResourceManager.GetString("DragDropDragComponents", resourceCulture); + } + } /// Looks up a localized string similar to This IDataObject doesn't support SetData.. + /// + public static string DragDropSetDataError { + get { + return ResourceManager.GetString("DragDropSetDataError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties.. + /// + public static string ExtenderProviderServiceDuplicateProvider { + get { + return ResourceManager.GetString("ExtenderProviderServiceDuplicateProvider", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Move {0}. + /// + public static string DragDropMoveComponent + { + get + { + return ResourceManager.GetString("DragDropMoveComponent", resourceCulture); + } + } /// Looks up a localized string similar to Read-Only. + /// + public static string InheritanceServiceReadOnlyCollection { + get { + return ResourceManager.GetString("InheritanceServiceReadOnlyCollection", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Move {0} components. + /// + public static string DragDropMoveComponents + { + get + { + return ResourceManager.GetString("DragDropMoveComponents", resourceCulture); + } + } /// Looks up a localized string similar to This method/object is not implemented by design.. + /// + public static string NotImplementedByDesign { + get { + return ResourceManager.GetString("NotImplementedByDesign", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Winforms Designer is not supported on this platform.. + /// + public static string PlatformNotSupported_WinformsDesigner { + get { + return ResourceManager.GetString("PlatformNotSupported_WinformsDesigner", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size {0}. + /// + public static string DragDropSizeComponent + { + get + { + return ResourceManager.GetString("DragDropSizeComponent", resourceCulture); + } + } /// Looks up a localized string similar to You cannot create a new session because this serialization manager already has an active serialization session.. + /// + public static string SerializationManagerAreadyInSession { + get { + return ResourceManager.GetString("SerializationManagerAreadyInSession", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size {0} components. + /// + public static string DragDropSizeComponents + { + get + { + return ResourceManager.GetString("DragDropSizeComponents", resourceCulture); + } + } /// Looks up a localized string similar to Duplicate declaration of member '{0}'. + /// + public static string SerializationManagerDuplicateComponentDecl { + get { + return ResourceManager.GetString("SerializationManagerDuplicateComponentDecl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The name '{0}' is already used by another object.. + /// + public static string SerializationManagerNameInUse { + get { + return ResourceManager.GetString("SerializationManagerNameInUse", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type '{0}' does not have a constructor with parameters of types {1}.. + /// + public static string SerializationManagerNoMatchingCtor { + get { + return ResourceManager.GetString("SerializationManagerNoMatchingCtor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This method cannot be invoked because the serialization manager does not have an active serialization session.. + /// + public static string SerializationManagerNoSession { + get { + return ResourceManager.GetString("SerializationManagerNoSession", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot name the object '{0}' because it is already named '{1}'.. + /// + public static string SerializationManagerObjectHasName { + get { + return ResourceManager.GetString("SerializationManagerObjectHasName", resourceCulture); + + } + } + + /// + /// Looks up a localized string similar to RTL_False. + /// + public static string RTL + { + get + { + return ResourceManager.GetString("RTL", resourceCulture); + } + } + /// Looks up a localized string similar to This method cannot be invoked because the serialization manager has an active serialization session.. + /// + public static string SerializationManagerWithinSession { + get { + return ResourceManager.GetString("SerializationManagerWithinSession", resourceCulture); + + } + } + + /// + + /// Looks up a localized string similar to Argument should be a non-empty string.. + /// + public static string ToolboxItemInvalidKey { + get { + return ResourceManager.GetString("ToolboxItemInvalidKey", resourceCulture); + } + } + /// Looks up a localized string similar to Elements of type {0} are not supported. The serializer expects the element to be one of the following: {1}.. + /// + public static string SerializerBadElementTypes { + get { + return ResourceManager.GetString("SerializerBadElementTypes", resourceCulture); + + } + } + + /// + /// Looks up a localized string similar to Property {0} requires an argument of type {1}.. + /// + public static string ToolboxItemInvalidPropertyType + { + get + { + return ResourceManager.GetString("ToolboxItemInvalidPropertyType", resourceCulture); + } + } + /// Looks up a localized string similar to The field '{0}' could not be found on the target object. Make sure that the field is defined as an instance variable on the target object and has the correct scope.. + /// + public static string SerializerFieldTargetEvalFailed { + get { + return ResourceManager.GetString("SerializerFieldTargetEvalFailed", resourceCulture); + + } + } + + /// + + /// Looks up a localized string similar to Toolbox item cannot be modified.. + /// + public static string ToolboxItemLocked { + get { + return ResourceManager.GetString("ToolboxItemLocked", resourceCulture); + } + } + + /// Looks up a localized string similar to Array rank '{0}' is too high. Visual Studio can only save and load arrays with a rank of 1.. + /// + public static string SerializerInvalidArrayRank { + get { + return ResourceManager.GetString("SerializerInvalidArrayRank", resourceCulture); + + } + } + + /// + + /// Looks up a localized string similar to Data type {0} is not serializable. Items added to a property dictionary must be serializable.. + /// + public static string ToolboxItemValueNotSerializable { + get { + return ResourceManager.GetString("ToolboxItemValueNotSerializable", resourceCulture); + } + } + /// Looks up a localized string similar to Code statements for the object '{0}' were lost during serialization. This may have been a result of another object misbehaving during serialization.. + /// + public static string SerializerLostStatements { + get { + return ResourceManager.GetString("SerializerLostStatements", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Members of type '{0}' cannot be serialized.. + /// + public static string SerializerMemberTypeNotSerializable { + get { + return ResourceManager.GetString("SerializerMemberTypeNotSerializable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The source code contains a reference to the class definition, but the class definition cannot be found.. + /// + public static string SerializerNoRootExpression { + get { + return ResourceManager.GetString("SerializerNoRootExpression", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Auto Arrange Tray Icons. + /// + public static string TrayAutoArrange + { + get + { + return ResourceManager.GetString("TrayAutoArrange", resourceCulture); + } + } + /// Looks up a localized string similar to The object '{0}' failed to serialize itself. It may not support code generation.. + /// + public static string SerializerNoSerializerForComponent { + get { + return ResourceManager.GetString("SerializerNoSerializerForComponent", resourceCulture); + } + } + + /// + + /// Looks up a localized string similar to Line Up Tray Icons. + /// + public static string TrayLineUpIcons { + get { + return ResourceManager.GetString("TrayLineUpIcons", resourceCulture); + } + } + /// Looks up a localized string similar to The type '{0}' has no event named '{1}'.. + /// + public static string SerializerNoSuchEvent { + get { + return ResourceManager.GetString("SerializerNoSuchEvent", resourceCulture); + + } + } + + /// + + /// Looks up a localized string similar to Show Large or Small Icons. + /// + public static string TrayShowLargeIcons { + get { + return ResourceManager.GetString("TrayShowLargeIcons", resourceCulture); + } +} + /// Looks up a localized string similar to The type '{0}' has no field named '{1}'.. + /// + public static string SerializerNoSuchField { + get { + return ResourceManager.GetString("SerializerNoSuchField", resourceCulture); + + } + } + + /// + + /// Looks up a localized string similar to Error. + /// + public static string UIServiceHelper_ErrorCaption { + get { + return ResourceManager.GetString("UIServiceHelper_ErrorCaption", resourceCulture); + } + } + /// Looks up a localized string similar to The type '{0}' has no property named '{1}'.. + /// + public static string SerializerNoSuchProperty { + get { + return ResourceManager.GetString("SerializerNoSuchProperty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error reading resources from the resource file for the culture {0}: {1}. + /// + public static string SerializerResourceException { + get { + return ResourceManager.GetString("SerializerResourceException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error reading resources from the resource file for the default culture: {0}. + /// + public static string SerializerResourceExceptionInvariant { + get { + return ResourceManager.GetString("SerializerResourceExceptionInvariant", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not find type '{0}'. Please make sure that the assembly that contains this type is referenced. If this type is a part of your development project, make sure that the project has been successfully built using settings for your current platform or Any CPU.. + /// + public static string SerializerTypeNotFound { + get { + return ResourceManager.GetString("SerializerTypeNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The variable '{0}' is either undeclared or was never assigned.. + /// + public static string SerializerUndeclaredName { + get { + return ResourceManager.GetString("SerializerUndeclaredName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type '{0}' is not available in the target framework.. + /// + public static string TypeNotFoundInTargetFramework { + get { + return ResourceManager.GetString("TypeNotFoundInTargetFramework", resourceCulture); + + } + } + + /// + /// Looks up a localized string similar to Add Component. + /// + public static string UndoEngineComponentAdd0 { + get { + return ResourceManager.GetString("UndoEngineComponentAdd0", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add {0}. + /// + public static string UndoEngineComponentAdd1 { + get { + return ResourceManager.GetString("UndoEngineComponentAdd1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change Component. + /// + public static string UndoEngineComponentChange0 { + get { + return ResourceManager.GetString("UndoEngineComponentChange0", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change {0}. + /// + public static string UndoEngineComponentChange1 { + get { + return ResourceManager.GetString("UndoEngineComponentChange1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change {0}.{1}. + /// + public static string UndoEngineComponentChange2 { + get { + return ResourceManager.GetString("UndoEngineComponentChange2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove Component. + /// + public static string UndoEngineComponentRemove0 { + get { + return ResourceManager.GetString("UndoEngineComponentRemove0", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove {0}. + /// + public static string UndoEngineComponentRemove1 { + get { + return ResourceManager.GetString("UndoEngineComponentRemove1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rename {0} to {1}. + /// + public static string UndoEngineComponentRename { + get { + return ResourceManager.GetString("UndoEngineComponentRename", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The service {0} is required but could not be found. If you have removed this service ensure that you provide a replacement.. + /// + public static string UndoEngineMissingService { + get { + return ResourceManager.GetString("UndoEngineMissingService", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not implemented.. + /// + public static string UnsafeNativeMethodsNotImplemented { + get { + return ResourceManager.GetString("UnsafeNativeMethodsNotImplemented", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <couldn't find resource string "WindowsFormsAddEvent">. + /// + public static string WindowsFormsAddEvent { + get { + return ResourceManager.GetString("WindowsFormsAddEvent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Horizontal center of {0} component(s). + /// + public static string WindowsFormsCommandCenterX { + get { + return ResourceManager.GetString("WindowsFormsCommandCenterX", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Vertical center of {0} component(s). + /// + public static string WindowsFormsCommandCenterY { + get { + return ResourceManager.GetString("WindowsFormsCommandCenterY", resourceCulture); + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/Resources/SR.resx b/src/System.Windows.Forms.Design/src/Resources/SR.resx index c76994d4e97..3d60caa0f0b 100644 --- a/src/System.Windows.Forms.Design/src/Resources/SR.resx +++ b/src/System.Windows.Forms.Design/src/Resources/SR.resx @@ -214,7 +214,115 @@ Not implemented. - Could not find method '{0}' + Could not find method '{0}'. + + + Format {0} components (alignment) + + + Align {0} components to grid + + + Cut {0} Components + + + Delete {0} components + + + An error occurred while processing this command.\r\n{0} + + + Format {0} components (spacing) + + + Paste components + + + Size {0} components + + + Size {0} components to grid + + + Unknown spacing command + + + Move {0} + + + Move {0} components + + + Horizontal center of {0} component(s) + + + Vertical center of {0} component(s) + + + Could not convert value '{0}' to the type '{1}'. + + + Could not find property '{0}' on '{1}'. + + + {0} Tasks + + + Error using the dropdown: {0} + + + Error invoking '{0}'. Details: {1} + + + Error setting value '{0}' to property '{1}'. Details: {2} + + + Inherited control + + + Inherited control (Private) + + + RTL_False + + + Auto Arrange Tray Icons + + + Line Up Tray Icons + + + Show Large or Small Icons + + + Error + + + <couldn't find resource string "WindowsFormsAddEvent"> + + + Copy and move {0} + + + Copy and move {0} Controls + + + Move {0} + + + Move {0} Controls + + + Call to BeginDrag must succeed before calling drag functions. + + + Drag {0} components + + + Size {0} + + + Size {0} components Add Component @@ -300,79 +408,16 @@ The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. - - Elements of type {0} are not supported. The serializer expects the element to be one of the following: {1}. - - - The field '{0}' could not be found on the target object. Make sure that the field is defined as an instance variable on the target object and has the correct scope. - - - The source code contains a reference to the class definition, but the class definition cannot be found. - - - The type '{0}' has no event named '{1}'. - - - The type '{0}' has no field named '{1}'. - - - The type '{0}' has no property named '{1}'. - - - Error reading resources from the resource file for the culture {0}: {1} - - - Error reading resources from the resource file for the default culture: {0} - - - Could not find type '{0}'. Please make sure that the assembly that contains this type is referenced. If this type is a part of your development project, make sure that the project has been successfully built using settings for your current platform or Any CPU. - - - The variable '{0}' is either undeclared or was never assigned. - - - Type '{0}' is not available in the target framework. - - - You cannot create a new session because this serialization manager already has an active serialization session. - - - Duplicate declaration of member '{0}' - - - The name '{0}' is already used by another object. - - - Type '{0}' does not have a constructor with parameters of types {1}. - - - This method cannot be invoked because the serialization manager does not have an active serialization session. - - - Cannot name the object '{0}' because it is already named '{1}'. - - - This method cannot be invoked because the serialization manager has an active serialization session. - - - Code statements for the object '{0}' were lost during serialization. This may have been a result of another object misbehaving during serialization. - - - The object '{0}' failed to serialize itself. It may not support code generation. - - - Array rank '{0}' is too high. Visual Studio can only save and load arrays with a rank of 1. - - - Members of type '{0}' cannot be serialized. + + Resize {0} - - Complete deserialization of {0} failed. + + Resize {0} Controls - - This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Dock in Parent Container - - The serialization store is closed. New objects cannot be added to a closed store. + + Undock in Parent Container diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf index c333765cdd4..0aa595223a5 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ Určuje, zda mají být v návrháři použity vodicí čáry. Pokud je tato vlastnost nastavena na hodnotu True, vodicí čáry budou použity jako vodítka. Pokud je tato vlastnost nastavena na hodnotu False, budou použity čáry mřížky. + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf index ad321646c69..aa40f830a0a 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ Steuert, ob Designer Ausrichtungslinien verwenden. Wenn "true", werden Ausrichtungslinien als Führungslinien verwendet. Wenn "false", werden Rasterlinien verwendet. + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf index d7137ea32a1..85969cf90ad 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ Controla si los diseñadores deberían utilizar líneas de ajuste. Si es true, las líneas de ajuste se utilizarán como guías. Si es false, se utilizarán líneas de cuadrícula. + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf index 04cd8b71975..241c3c2c03f 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ Contrôle si les concepteurs doivent utiliser des lignes d'alignement. Si tel est le cas, les lignes d'alignement sont utilisées à titre de guides. Dans le cas contraire, un quadrillage est utilisé. + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf index 4f255d476df..05fbe81c5d3 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ Controlla se nelle finestre di progettazione devono essere utilizzate guide di allineamento. Se è true, le guide di allineamento verranno utilizzate come riferimento. In caso contrario, verranno utilizzare le linee della griglia. + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf index ccd31968af1..4158a17e30c 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ デザイナーでスナップ線を使用するかどうかを制御します。 true の場合、スナップ線がガイドとして使用されます。 false の場合、グリッド線が使用されます。 + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf index 1e06f8b478e..a2252a60dea 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ 디자이너에서 맞춤선을 사용할지 여부를 제어합니다. true이면 맞춤선이 안내선으로 사용되고 false이면 모눈선이 사용됩니다. + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf index 8e04aa4658b..32a069798c2 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ Określa, czy projektanci mają używać linii przyciągania. Jeśli tak, linie przyciągania będą używane jako prowadnice. Jeśli nie, będą używane linie siatki. + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf index dc29c6a82d2..722b8d90f1f 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ Controla se os designers devem usar linhas de ajuste. Se verdadeiro, essas linhas serão usadas como guias. Se falso, as linhas de grade serão usadas em vez das linhas de ajuste. + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf index b8a6f7ac3fc..525c4bfb635 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ Определяет, будут ли конструкторы использовать линии выравнивания. Если задано значение true, линии выравнивания используются в качестве направляющих. В случае false используются линии сетки. + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf index cb19f5787c8..098803be787 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ Tasarımcıların dayama çizgileri kullanıp kullanmayacağını denetler. True ise, dayama çizgileri kılavuz olarak kullanılır. False ise, kılavuz çizgileri kullanılır. + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf index a86e3506982..2be88d9ccbe 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ 控制设计器是否应使用对齐线。如果设置为 True,则使用对齐线作为参考线。如果设置为 False,则使用网格线。 + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf index 21a336230b0..3ce02aabe46 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf @@ -17,6 +17,16 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + Adding event '{0}' Adding event '{0}' @@ -222,6 +232,16 @@ 控制設計工具是否應使用對齊線。如果為 true,就會使用對齊線來對齊。如果為 false,就會使用格線。 + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET Component diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs index 3dea94441df..9501ebbf41d 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs @@ -2,8 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; +using System.Drawing.Design; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Security; +using Microsoft.Win32; namespace System.Windows.Forms.Design.Behavior { @@ -23,30 +33,295 @@ namespace System.Windows.Forms.Design.Behavior /// public sealed class BehaviorService : IDisposable { + private readonly IServiceProvider _serviceProvider; //standard service provider + private readonly AdornerWindow _adornerWindow; //the transparent window all glyphs are drawn to + private readonly BehaviorServiceAdornerCollection _adorners; //we manage all adorners (glyph-containers) here + private readonly ArrayList _behaviorStack; //the stack behavior objects can be pushed to and popped from + private Behavior _captureBehavior; //the behavior that currently has capture; may be null + private Glyph _hitTestedGlyph; //the last valid glyph that was hit tested + private IToolboxService _toolboxSvc; //allows us to have the toolbox choose a cursor + private Control _dropSource; //actual control used to call .dodragdrop + private DragEventArgs _validDragArgs; //if valid - this is used to fabricate drag enter/leave envents + private BehaviorDragDropEventHandler _beginDragHandler; //fired directly before we call .DoDragDrop() + private BehaviorDragDropEventHandler _endDragHandler; //fired directly after we call .DoDragDrop() + private EventHandler _synchronizeEventHandler; //fired when we want to synchronize the selection + private NativeMethods.TRACKMOUSEEVENT _trackMouseEvent; //demand created (once) used to track the mouse hover event + private bool _trackingMouseEvent; //state identifying current mouse tracking + private string[] _testHook_RecentSnapLines; //we keep track of the last snaplines we found - for testing purposes + private readonly MenuCommandHandler _menuCommandHandler; //private object that handles all menu commands + private bool _useSnapLines; //indicates if this designer session is using snaplines or snapping to a grid + private bool _queriedSnapLines; //only query for this once since we require the user restart design sessions when this changes + private readonly Hashtable _dragEnterReplies; // we keep track of whether glyph has already responded to a DragEnter this D&D. + private static readonly TraceSwitch s_dragDropSwitch = new TraceSwitch("BSDRAGDROP", "Behavior service drag & drop messages"); + + private bool _dragging = false; // are we in a drag + private bool _cancelDrag = false; // should we cancel the drag on the next QueryContinueDrag + + + private int _adornerWindowIndex = -1; + + //test hooks for SnapLines + private static int WM_GETALLSNAPLINES; + private static int WM_GETRECENTSNAPLINES; + + private DesignerActionUI _actionPointer; // pointer to the designer action service so we can supply mouse over notifications + + private const string ToolboxFormat = ".NET Toolbox Item"; // used to detect if a drag is coming from the toolbox. [SuppressMessage("Microsoft.Performance", "CA1805:DoNotInitializeUnnecessarily")] internal BehaviorService(IServiceProvider serviceProvider, Control windowFrame) { - throw new NotImplementedException(SR.NotImplementedByDesign); + _serviceProvider = serviceProvider; +//create the AdornerWindow + _adornerWindow = new AdornerWindow(this, windowFrame); + + //use the adornerWindow as an overlay + IOverlayService os = (IOverlayService)serviceProvider.GetService(typeof(IOverlayService)); + if (os != null) + { + _adornerWindowIndex = os.PushOverlay(_adornerWindow); + } + + _dragEnterReplies = new Hashtable(); + + //start with an empty adorner collection & no behavior on the stack + _adorners = new BehaviorServiceAdornerCollection(this); + _behaviorStack = new ArrayList(); + + _hitTestedGlyph = null; + _validDragArgs = null; + _actionPointer = null; + _trackMouseEvent = null; + _trackingMouseEvent = false; + + //create out object that will handle all menucommands + IMenuCommandService menuCommandService = serviceProvider.GetService(typeof(IMenuCommandService)) as IMenuCommandService; + IDesignerHost host = serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; + + if (menuCommandService != null && host != null) + { + + _menuCommandHandler = new MenuCommandHandler(this, menuCommandService); + + host.RemoveService(typeof(IMenuCommandService)); + host.AddService(typeof(IMenuCommandService), _menuCommandHandler); + } + + //default layoutmode is SnapToGrid. + _useSnapLines = false; + _queriedSnapLines = false; + + //test hooks + WM_GETALLSNAPLINES = SafeNativeMethods.RegisterWindowMessage("WM_GETALLSNAPLINES"); + WM_GETRECENTSNAPLINES = SafeNativeMethods.RegisterWindowMessage("WM_GETRECENTSNAPLINES"); + + // Listen to the SystemEvents so that we can resync selection based on display settings etc. + SystemEvents.DisplaySettingsChanged += new EventHandler(OnSystemSettingChanged); + SystemEvents.InstalledFontsChanged += new EventHandler(OnSystemSettingChanged); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); } /// /// Read-only property that returns the AdornerCollection that the BehaivorService manages. /// - public BehaviorServiceAdornerCollection Adorners => - throw new NotImplementedException(SR.NotImplementedByDesign); + public BehaviorServiceAdornerCollection Adorners + { + get => _adorners; + } + + /// + /// Returns the actual Control that represents the transparent AdornerWindow. + /// + internal Control AdornerWindowControl + { + get => _adornerWindow; + } + + internal bool HasCapture + { + get => _captureBehavior != null; + } + + /// + /// Returns the LayoutMode setting of the current designer session. Either SnapLines or SnapToGrid. + /// + internal bool UseSnapLines + { + get + { + //we only check for this service/value once since we require the user to re-open the designer session after these types of option have been modified + if (!_queriedSnapLines) + { + _queriedSnapLines = true; + _useSnapLines = DesignerUtils.UseSnapLines(_serviceProvider); + } + + return _useSnapLines; + } + } /// /// Creates and returns a Graphics object for the AdornerWindow /// - public Graphics AdornerWindowGraphics => throw new NotImplementedException(SR.NotImplementedByDesign); + public Graphics AdornerWindowGraphics + { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get + { + Graphics result = _adornerWindow.CreateGraphics(); + result.Clip = new Region(_adornerWindow.DesignerFrameDisplayRectangle); + return result; + } + } - public Behavior CurrentBehavior => throw new NotImplementedException(SR.NotImplementedByDesign); + public Behavior CurrentBehavior + { + get + { + if (_behaviorStack != null && _behaviorStack.Count > 0) + { + return (_behaviorStack[0] as Behavior); + } + else + { + return null; + } + } + } + + internal bool CancelDrag + { + get => _cancelDrag; + set => _cancelDrag = value; + } + internal DesignerActionUI DesignerActionUI + { + get => _actionPointer; + set => _actionPointer = value; + } + /// + /// Called by the DragAssistanceManager after a snapline/drag op has completed - we store this data for testing purposes. See TestHook_GetRecentSnapLines method. + /// + internal string[] RecentSnapLines + { + set => _testHook_RecentSnapLines = value; + } /// /// Disposes the behavior service. /// public void Dispose() { - throw new NotImplementedException(SR.NotImplementedByDesign); + // remove adorner window from overlay service + IOverlayService os = (IOverlayService)_serviceProvider.GetService(typeof(IOverlayService)); + if (os != null) + { + os.RemoveOverlay(_adornerWindow); + } + + MenuCommandHandler menuCommandHandler = null; + if (_serviceProvider.GetService(typeof(IMenuCommandService)) is IMenuCommandService menuCommandService) + menuCommandHandler = menuCommandService as MenuCommandHandler; + + if (menuCommandHandler != null && _serviceProvider.GetService(typeof(IDesignerHost)) is IDesignerHost host) + { + IMenuCommandService oldMenuCommandService = menuCommandHandler.MenuService; + host.RemoveService(typeof(IMenuCommandService)); + host.AddService(typeof(IMenuCommandService), oldMenuCommandService); + } + + _adornerWindow.Dispose(); + SystemEvents.DisplaySettingsChanged -= new EventHandler(OnSystemSettingChanged); + SystemEvents.InstalledFontsChanged -= new EventHandler(OnSystemSettingChanged); + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + } + + private Control DropSource + { + get + { + if (_dropSource == null) + { + _dropSource = new Control(); + } + return _dropSource; + } + } + + internal DragDropEffects DoDragDrop(DropSourceBehavior dropSourceBehavior) + { + //hook events + DropSource.QueryContinueDrag += new QueryContinueDragEventHandler(dropSourceBehavior.QueryContinueDrag); + DropSource.GiveFeedback += new GiveFeedbackEventHandler(dropSourceBehavior.GiveFeedback); + + DragDropEffects res = DragDropEffects.None; + + //build up the eventargs for firing our dragbegin/end events + ICollection dragComponents = ((DropSourceBehavior.BehaviorDataObject)dropSourceBehavior.DataObject).DragComponents; + BehaviorDragDropEventArgs eventArgs = new BehaviorDragDropEventArgs(dragComponents); + + try + { + try + { + OnBeginDrag(eventArgs); + _dragging = true; + _cancelDrag = false; + // This is normally cleared on OnMouseUp, but we might not get an OnMouseUp to clear it. VSWhidbey #474259 + // So let's make sure it is really cleared when we start the drag. + _dragEnterReplies.Clear(); + res = DropSource.DoDragDrop(dropSourceBehavior.DataObject, dropSourceBehavior.AllowedEffects); + } + finally + { + DropSource.QueryContinueDrag -= new QueryContinueDragEventHandler(dropSourceBehavior.QueryContinueDrag); + DropSource.GiveFeedback -= new GiveFeedbackEventHandler(dropSourceBehavior.GiveFeedback); + //If the drop gets cancelled, we won't get a OnDragDrop, so let's make sure that we stop + //processing drag notifications. Also VSWhidbey #354552 and 133339. + EndDragNotification(); + _validDragArgs = null; + _dragging = false; + _cancelDrag = false; + OnEndDrag(eventArgs); + } + } + catch (CheckoutException cex) + { + if (cex == CheckoutException.Canceled) + { + res = DragDropEffects.None; + } + else + { + throw; + } + } + finally + { + // It's possible we did not receive an EndDrag, and therefore we weren't able to cleanup the drag. We will do that here. Scenarios where this happens: dragging from designer to recycle-bin, or over the taskbar. + if (dropSourceBehavior != null) + { + dropSourceBehavior.CleanupDrag(); + } + } + return res; + } + + internal void EndDragNotification() + { + _adornerWindow.EndDragNotification(); + } + + private void OnEndDrag(BehaviorDragDropEventArgs e) + { + _endDragHandler?.Invoke(this, e); + } + + private void OnBeginDrag(BehaviorDragDropEventArgs e) + { + if (_beginDragHandler != null) + { + _beginDragHandler(this, e); + } } /// @@ -54,7 +329,9 @@ public void Dispose() /// public Point AdornerWindowPointToScreen(Point p) { - throw new NotImplementedException(SR.NotImplementedByDesign); + NativeMethods.POINT offset = new NativeMethods.POINT(p.X, p.Y); + NativeMethods.MapWindowPoints(_adornerWindow.Handle, IntPtr.Zero, offset, 1); + return new Point(offset.x, offset.y); } /// @@ -62,7 +339,8 @@ public Point AdornerWindowPointToScreen(Point p) /// public Point AdornerWindowToScreen() { - throw new NotImplementedException(SR.NotImplementedByDesign); + Point origin = new Point(0, 0); + return AdornerWindowPointToScreen(origin); } /// @@ -70,7 +348,20 @@ public Point AdornerWindowToScreen() /// public Point ControlToAdornerWindow(Control c) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (c.Parent == null) + { + return Point.Empty; + } + + NativeMethods.POINT pt = new NativeMethods.POINT(); + pt.x = c.Left; + pt.y = c.Top; + NativeMethods.MapWindowPoints(c.Parent.Handle, _adornerWindow.Handle, pt, 1); + if (c.Parent.IsMirrored) + { + pt.x -= c.Width; + } + return new Point(pt.x, pt.y); } /// @@ -78,7 +369,11 @@ public Point ControlToAdornerWindow(Control c) /// public Point MapAdornerWindowPoint(IntPtr handle, Point pt) { - throw new NotImplementedException(SR.NotImplementedByDesign); + NativeMethods.POINT nativePoint = new NativeMethods.POINT(); + nativePoint.x = pt.X; + nativePoint.y = pt.Y; + NativeMethods.MapWindowPoints(handle, _adornerWindow.Handle, nativePoint, 1); + return new Point(nativePoint.x, nativePoint.y); } /// @@ -86,7 +381,13 @@ public Point MapAdornerWindowPoint(IntPtr handle, Point pt) /// public Rectangle ControlRectInAdornerWindow(Control c) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (c.Parent == null) + { + return Rectangle.Empty; + } + Point loc = ControlToAdornerWindow(c); + + return new Rectangle(loc, c.Size); } /// @@ -95,8 +396,14 @@ public Rectangle ControlRectInAdornerWindow(Control c) /// public event BehaviorDragDropEventHandler BeginDrag { - add => throw new NotImplementedException(SR.NotImplementedByDesign); - remove => throw new NotImplementedException(SR.NotImplementedByDesign); + add + { + _beginDragHandler += value; + } + remove + { + _beginDragHandler -= value; + } } /// @@ -105,8 +412,14 @@ public event BehaviorDragDropEventHandler BeginDrag /// public event BehaviorDragDropEventHandler EndDrag { - add => throw new NotImplementedException(SR.NotImplementedByDesign); - remove => throw new NotImplementedException(SR.NotImplementedByDesign); + add + { + _endDragHandler += value; + } + remove + { + _endDragHandler -= value; + } } /// @@ -114,9 +427,15 @@ public event BehaviorDragDropEventHandler EndDrag /// public event EventHandler Synchronize { - add => throw new NotImplementedException(SR.NotImplementedByDesign); + add + { + _synchronizeEventHandler += value; + } - remove => throw new NotImplementedException(SR.NotImplementedByDesign); + remove + { + _synchronizeEventHandler -= value; + } } /// @@ -126,7 +445,24 @@ public event EventHandler Synchronize /// public Behavior GetNextBehavior(Behavior behavior) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (_behaviorStack != null && _behaviorStack.Count > 0) + { + int index = _behaviorStack.IndexOf(behavior); + if ((index != -1) && (index < _behaviorStack.Count - 1)) + { + return _behaviorStack[index + 1] as Behavior; + } + } + return null; + } + + internal void EnableAllAdorners(bool enabled) + { + foreach (Adorner adorner in Adorners) + { + adorner.EnabledInternal = enabled; + } + Invalidate(); } /// @@ -135,7 +471,7 @@ public Behavior GetNextBehavior(Behavior behavior) /// public void Invalidate() { - throw new NotImplementedException(SR.NotImplementedByDesign); + _adornerWindow.InvalidateAdornerWindow(); } /// @@ -144,7 +480,7 @@ public void Invalidate() /// public void Invalidate(Rectangle rect) { - throw new NotImplementedException(SR.NotImplementedByDesign); + _adornerWindow.InvalidateAdornerWindow(rect); } /// @@ -153,7 +489,7 @@ public void Invalidate(Rectangle rect) /// public void Invalidate(Region r) { - throw new NotImplementedException(SR.NotImplementedByDesign); + _adornerWindow.InvalidateAdornerWindow(r); } /// @@ -161,7 +497,10 @@ public void Invalidate(Region r) /// public void SyncSelection() { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (_synchronizeEventHandler != null) + { + _synchronizeEventHandler(this, EventArgs.Empty); + } } /// @@ -169,7 +508,30 @@ public void SyncSelection() /// public Behavior PopBehavior(Behavior behavior) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (_behaviorStack.Count == 0) + { + throw new InvalidOperationException(); + } + + int index = _behaviorStack.IndexOf(behavior); + if (index == -1) + { + Debug.Assert(false, "Could not find the behavior to pop - did it already get popped off? " + behavior.ToString()); + return null; + } + + _behaviorStack.RemoveAt(index); + if (behavior == _captureBehavior) + { + _adornerWindow.Capture = false; + // Defensive: adornerWindow should get a WM_CAPTURECHANGED, but do this by hand if it didn't. + if (_captureBehavior != null) + { + OnLoseCapture(); + Debug.Assert(_captureBehavior == null, "OnLostCapture should have cleared captureBehavior"); + } + } + return behavior; } /// @@ -178,7 +540,18 @@ public Behavior PopBehavior(Behavior behavior) /// public void PushBehavior(Behavior behavior) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (behavior == null) + { + throw new ArgumentNullException("behavior"); + } + + // Should we catch this + _behaviorStack.Insert(0, behavior); + // If there is a capture behavior, and it isn't this behavior, notify it that it no longer has capture. + if (_captureBehavior != null && _captureBehavior != behavior) + { + OnLoseCapture(); + } } /// @@ -188,7 +561,20 @@ public void PushBehavior(Behavior behavior) /// public void PushCaptureBehavior(Behavior behavior) { - throw new NotImplementedException(SR.NotImplementedByDesign); + PushBehavior(behavior); + _captureBehavior = behavior; + _adornerWindow.Capture = true; + + //VSWhidbey #373836. Since we are now capturing all mouse messages, we might miss some WM_MOUSEACTIVATE which would have activated the app. So if the DialogOwnerWindow (e.g. VS) is not the active window, let's activate it here. + IUIService uiService = (IUIService)_serviceProvider.GetService(typeof(IUIService)); + if (uiService != null) + { + IWin32Window hwnd = uiService.GetDialogOwnerWindow(); + if (hwnd != null && hwnd.Handle != IntPtr.Zero && hwnd.Handle != UnsafeNativeMethods.GetActiveWindow()) + { + UnsafeNativeMethods.SetActiveWindow(new HandleRef(this, hwnd.Handle)); + } + } } /// @@ -196,7 +582,1219 @@ public void PushCaptureBehavior(Behavior behavior) /// public Point ScreenToAdornerWindow(Point p) { - throw new NotImplementedException(SR.NotImplementedByDesign); + NativeMethods.POINT offset = new NativeMethods.POINT(); + offset.x = p.X; + offset.y = p.Y; + NativeMethods.MapWindowPoints(IntPtr.Zero, _adornerWindow.Handle, offset, 1); + return new Point(offset.x, offset.y); + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + internal void OnLoseCapture() + { + if (_captureBehavior != null) + { + Behavior b = _captureBehavior; + _captureBehavior = null; + try + { + b.OnLoseCapture(_hitTestedGlyph, EventArgs.Empty); + } + catch + { + } + } + } + + /// + /// The AdornerWindow is a transparent window that resides ontop of the Designer's Frame. This window is used by the BehaviorService to intercept all messages. It also serves as a unified canvas on which to paint Glyphs. + /// + private class AdornerWindow : Control + { + private BehaviorService _behaviorService;//ptr back to BehaviorService + private Control _designerFrame;//the designer's frame + private static MouseHook s_mouseHook; // shared mouse hook + private static List s_adornerWindowList = new List(); + private bool _processingDrag; // is this particular window in a drag operation + + /// + /// Constructor that parents itself to the Designer Frame and hooks all + /// necessary events. + /// + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + internal AdornerWindow(BehaviorService behaviorService, Control designerFrame) + { + this._behaviorService = behaviorService; + this._designerFrame = designerFrame; + Dock = DockStyle.Fill; + AllowDrop = true; + Text = "AdornerWindow"; + SetStyle(ControlStyles.Opaque, true); + } + + /// + /// The key here is to set the appropriate TransparetWindow style. + /// + protected override CreateParams CreateParams + { + get + { + CreateParams cp = base.CreateParams; + cp.Style &= ~(NativeMethods.WS_CLIPCHILDREN | NativeMethods.WS_CLIPSIBLINGS); + cp.ExStyle |= 0x00000020/*WS_EX_TRANSPARENT*/; + return cp; + } + } + + internal bool ProcessingDrag + { + get => _processingDrag; + set => _processingDrag = value; + } + + /// + /// We'll use CreateHandle as our notification for creating our mouse hooker. + /// + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + s_adornerWindowList.Add(this); + if (s_mouseHook == null) + { + s_mouseHook = new MouseHook(); + } + } + + /// + /// Unhook and null out our mouseHook. + /// + protected override void OnHandleDestroyed(EventArgs e) + { + s_adornerWindowList.Remove(this); + // unregister the mouse hook once all adorner windows have been disposed. + if (s_adornerWindowList.Count == 0 && s_mouseHook != null) + { + s_mouseHook.Dispose(); + s_mouseHook = null; + } + base.OnHandleDestroyed(e); + } + + /// + /// Null out our mouseHook and unhook any events. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_designerFrame != null) + { + _designerFrame = null; + } + } + base.Dispose(disposing); + } + + /// + /// Returns true if the DesignerFrame is created & not being disposed. + /// + internal Control DesignerFrame + { + get => _designerFrame; + } + + /// + /// Returns the display rectangle for the adorner window + /// + internal Rectangle DesignerFrameDisplayRectangle + { + get + { + if (DesignerFrameValid) + { + return ((DesignerFrame)_designerFrame).DisplayRectangle; + } + else + { + return Rectangle.Empty; + } + } + } + + /// + /// Returns true if the DesignerFrame is created & not being disposed. + /// + internal bool DesignerFrameValid + { + get + { + if (_designerFrame == null || _designerFrame.IsDisposed || !_designerFrame.IsHandleCreated) + { + return false; + } + return true; + } + } + + public IEnumerable Adorners { get; private set; } + + /// + /// Ultimately called by ControlDesigner when it receives a DragDrop message - here, we'll exit from 'drag mode'. + /// + internal void EndDragNotification() + { + ProcessingDrag = false; + } + + /// + /// Invalidates the transparent AdornerWindow by asking the Designer Frame beneath it to invalidate. Note the they use of the .Update() call for perf. purposes. + /// + internal void InvalidateAdornerWindow() + { + if (DesignerFrameValid) + { + _designerFrame.Invalidate(true); + _designerFrame.Update(); + } + } + + /// + /// Invalidates the transparent AdornerWindow by asking the Designer Frame beneath it to invalidate. Note the they use of the .Update() call for perf. purposes. + /// + internal void InvalidateAdornerWindow(Region region) + { + if (DesignerFrameValid) + { + //translate for non-zero scroll positions + Point scrollPosition = ((DesignerFrame)_designerFrame).AutoScrollPosition; + region.Translate(scrollPosition.X, scrollPosition.Y); + + _designerFrame.Invalidate(region, true); + _designerFrame.Update(); + } + } + + /// + /// Invalidates the transparent AdornerWindow by asking the Designer Frame beneath it to invalidate. Note the they use of the .Update() call for perf. purposes. + /// + internal void InvalidateAdornerWindow(Rectangle rectangle) + { + if (DesignerFrameValid) + { + //translate for non-zero scroll positions + Point scrollPosition = ((DesignerFrame)_designerFrame).AutoScrollPosition; + rectangle.Offset(scrollPosition.X, scrollPosition.Y); + + _designerFrame.Invalidate(rectangle, true); + _designerFrame.Update(); + } + } + + /// + /// The AdornerWindow hooks all Drag/Drop notification so they can be forwarded to the appropriate Behavior via the BehaviorService. + /// + protected override void OnDragDrop(DragEventArgs e) + { + try + { + _behaviorService.OnDragDrop(e); + } + finally + { + ProcessingDrag = false; + } + } + + internal void EnableAllAdorners(bool enabled) + { + foreach (Adorner adorner in Adorners) + { + adorner.EnabledInternal = enabled; + } + Invalidate(); + } + + private static bool IsLocalDrag(DragEventArgs e) + { + if (e.Data is DropSourceBehavior.BehaviorDataObject) + { + return true; + } + else + { + // Gets all the data formats and data conversion formats in the data object. + string[] allFormats = e.Data.GetFormats(); + + for (int i = 0; i < allFormats.Length; i++) + { + if (allFormats[i].Length == ToolboxFormat.Length && + string.Equals(ToolboxFormat, allFormats[i])) + { + return true; + } + } + } + return false; + } + + /// + /// The AdornerWindow hooks all Drag/Drop notification so they can be forwarded to the appropriate Behavior via the BehaviorService. + /// + protected override void OnDragEnter(DragEventArgs e) + { + ProcessingDrag = true; + + // determine if this is a local drag, if it is, do normal processing otherwise, force a PropagateHitTest. We need to force this because the OLE D&D service suspends mouse messages when the drag is not local so the mouse hook never sees them. + if (!IsLocalDrag(e)) + { + _behaviorService._validDragArgs = e; + NativeMethods.POINT pt = new NativeMethods.POINT(); + NativeMethods.GetCursorPos(pt); + NativeMethods.MapWindowPoints(IntPtr.Zero, Handle, pt, 1); + Point mousePos = new Point(pt.x, pt.y); + _behaviorService.PropagateHitTest(mousePos); + + } + _behaviorService.OnDragEnter(null, e); + } + + /// + /// The AdornerWindow hooks all Drag/Drop notification so they can be forwarded to the appropriate Behavior via the BehaviorService. + /// + protected override void OnDragLeave(EventArgs e) + { + // set our dragArgs to null so we know not to send drag enter/leave events when we re-enter the dragging area + _behaviorService._validDragArgs = null; + try + { + _behaviorService.OnDragLeave(null, e); + } + finally + { + ProcessingDrag = false; + } + } + + /// + /// The AdornerWindow hooks all Drag/Drop notification so they can be forwarded to the appropriate Behavior via the BehaviorService. + /// + protected override void OnDragOver(DragEventArgs e) + { + ProcessingDrag = true; + if (!IsLocalDrag(e)) + { + _behaviorService._validDragArgs = e; + NativeMethods.POINT pt = new NativeMethods.POINT(); + NativeMethods.GetCursorPos(pt); + NativeMethods.MapWindowPoints(IntPtr.Zero, Handle, pt, 1); + Point mousePos = new Point(pt.x, pt.y); + _behaviorService.PropagateHitTest(mousePos); + } + + _behaviorService.OnDragOver(e); + } + + /// + /// The AdornerWindow hooks all Drag/Drop notification so they can be forwarded to the appropriate Behavior via the BehaviorService. + /// + protected override void OnGiveFeedback(GiveFeedbackEventArgs e) + { + _behaviorService.OnGiveFeedback(e); + } + + /// + /// The AdornerWindow hooks all Drag/Drop notification so they can be forwarded to the appropriate Behavior via the BehaviorService. + /// + protected override void OnQueryContinueDrag(QueryContinueDragEventArgs e) + { + _behaviorService.OnQueryContinueDrag(e); + } + + /// + /// Called by ControlDesigner when it receives a DragEnter message - we'll let listen to all Mouse Messages so we can send drag notifcations. + /// + internal void StartDragNotification() + { + ProcessingDrag = true; + } + + /// + /// The AdornerWindow intercepts all designer-related messages and forwards them to the BehaviorService for appropriate actions. Note that Paint and HitTest messages are correctly parsed and translated to AdornerWindow coords. + /// + protected override void WndProc(ref Message m) + { + //special test hooks + if (m.Msg == BehaviorService.WM_GETALLSNAPLINES) + { + _behaviorService.TestHook_GetAllSnapLines(ref m); + } + else if (m.Msg == BehaviorService.WM_GETRECENTSNAPLINES) + { + _behaviorService.TestHook_GetRecentSnapLines(ref m); + } + + switch (m.Msg) + { + case NativeMethods.WM_PAINT: + // Stash off the region we have to update + IntPtr hrgn = NativeMethods.CreateRectRgn(0, 0, 0, 0); + NativeMethods.GetUpdateRgn(m.HWnd, hrgn, true); + // The region we have to update in terms of the smallest rectangle that completely encloses the update region of the window gives us the clip rectangle + NativeMethods.RECT clip = new NativeMethods.RECT(); + NativeMethods.GetUpdateRect(m.HWnd, ref clip, true); + Rectangle paintRect = new Rectangle(clip.left, clip.top, clip.right - clip.left, clip.bottom - clip.top); + + try + { + using (Region r = Region.FromHrgn(hrgn)) + { + // Call the base class to do its painting. + DefWndProc(ref m); + // Now do our own painting. + using (Graphics g = Graphics.FromHwnd(m.HWnd)) + { + using (PaintEventArgs pevent = new PaintEventArgs(g, paintRect)) + { + g.Clip = r; + _behaviorService.PropagatePaint(pevent); + } + } + } + } + finally + { + NativeMethods.DeleteObject(hrgn); + } + break; + + case NativeMethods.WM_NCHITTEST: + Point pt = new Point((short)NativeMethods.Util.LOWORD(unchecked((int)(long)m.LParam)), + (short)NativeMethods.Util.HIWORD(unchecked((int)(long)m.LParam))); + NativeMethods.POINT pt1 = new NativeMethods.POINT + { + x = 0, + y = 0 + }; + NativeMethods.MapWindowPoints(IntPtr.Zero, Handle, pt1, 1); + pt.Offset(pt1.x, pt1.y); + if (_behaviorService.PropagateHitTest(pt) && !ProcessingDrag) + { + m.Result = (IntPtr)(NativeMethods.HTTRANSPARENT); + } + else + { + m.Result = (IntPtr)(NativeMethods.HTCLIENT); + } + break; + + case NativeMethods.WM_CAPTURECHANGED: + base.WndProc(ref m); + _behaviorService.OnLoseCapture(); + break; + + default: + base.WndProc(ref m); + break; + } + } + + /// + /// Called by our mouseHook when it spies a mouse message that the adornerWindow would be interested in. + /// Returning 'true' signifies that the message was processed and should not continue to child windows. + /// + private bool WndProcProxy(ref Message m, int x, int y) + { + Point mouseLoc = new Point(x, y); + _behaviorService.PropagateHitTest(mouseLoc); + switch (m.Msg) + { + case NativeMethods.WM_LBUTTONDOWN: + if (_behaviorService.OnMouseDown(MouseButtons.Left, mouseLoc)) + { + return false; + } + break; + + case NativeMethods.WM_RBUTTONDOWN: + if (_behaviorService.OnMouseDown(MouseButtons.Right, mouseLoc)) + { + return false; + } + break; + + case NativeMethods.WM_MOUSEMOVE: + if (_behaviorService.OnMouseMove(Control.MouseButtons, mouseLoc)) + { + return false; + } + break; + + case NativeMethods.WM_LBUTTONUP: + if (_behaviorService.OnMouseUp(MouseButtons.Left)) + { + return false; + } + break; + + case NativeMethods.WM_RBUTTONUP: + if (_behaviorService.OnMouseUp(MouseButtons.Right)) + { + return false; + } + break; + + case NativeMethods.WM_MOUSEHOVER: + if (_behaviorService.OnMouseHover(mouseLoc)) + { + return false; + } + break; + + case NativeMethods.WM_LBUTTONDBLCLK: + if (_behaviorService.OnMouseDoubleClick(MouseButtons.Left, mouseLoc)) + { + return false; + } + break; + + case NativeMethods.WM_RBUTTONDBLCLK: + if (_behaviorService.OnMouseDoubleClick(MouseButtons.Right, mouseLoc)) + { + return false; + } + break; + } + return true; + } + + /// + /// This class knows how to hook all the messages to a given process/thread. + /// On any mouse clicks, it asks the designer what to do with the message, that is to eat it or propogate it to the control it was meant for. This allows us to synchrounously process mouse messages when the AdornerWindow itself may be pumping messages. + /// + [SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] + private class MouseHook + { + private AdornerWindow _currentAdornerWindow; + private int _thisProcessID = 0; + private GCHandle _mouseHookRoot; + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + private IntPtr _mouseHookHandle = IntPtr.Zero; + private bool _processingMessage; + + private bool _isHooked = false; //VSWHIDBEY # 474112 + private int _lastLButtonDownTimeStamp; + + public MouseHook() + { +#if DEBUG + _callingStack = Environment.StackTrace; +#endif + HookMouse(); + } +#if DEBUG + readonly string _callingStack; + ~MouseHook() + { + Debug.Assert(_mouseHookHandle == IntPtr.Zero, "Finalizing an active mouse hook. This will crash the process. Calling stack: " + _callingStack); + } +#endif + + public void Dispose() + { + UnhookMouse(); + } + + private void HookMouse() + { + Debug.Assert(AdornerWindow.s_adornerWindowList.Count > 0, "No AdornerWindow available to create the mouse hook"); + lock (this) + { + if (_mouseHookHandle != IntPtr.Zero || AdornerWindow.s_adornerWindowList.Count == 0) + { + return; + } + + if (_thisProcessID == 0) + { + AdornerWindow adornerWindow = AdornerWindow.s_adornerWindowList[0]; + UnsafeNativeMethods.GetWindowThreadProcessId(new HandleRef(adornerWindow, adornerWindow.Handle), out _thisProcessID); + } + + NativeMethods.HookProc hook = new NativeMethods.HookProc(MouseHookProc); + _mouseHookRoot = GCHandle.Alloc(hook); + +#pragma warning disable 618 + _mouseHookHandle = UnsafeNativeMethods.SetWindowsHookEx(NativeMethods.WH_MOUSE, + hook, + new HandleRef(null, IntPtr.Zero), + AppDomain.GetCurrentThreadId()); +#pragma warning restore 618 + if (_mouseHookHandle != IntPtr.Zero) + { + _isHooked = true; + } + Debug.Assert(_mouseHookHandle != IntPtr.Zero, "Failed to install mouse hook"); + } + } + + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private unsafe IntPtr MouseHookProc(int nCode, IntPtr wparam, IntPtr lparam) + { + if (_isHooked && nCode == NativeMethods.HC_ACTION) + { + NativeMethods.MOUSEHOOKSTRUCT mhs = (NativeMethods.MOUSEHOOKSTRUCT)UnsafeNativeMethods.PtrToStructure(lparam, typeof(NativeMethods.MOUSEHOOKSTRUCT)); + if (mhs != null) + { + try + { + if (ProcessMouseMessage(mhs.hWnd, unchecked((int)(long)wparam), mhs.pt_x, mhs.pt_y)) + { + return (IntPtr)1; + } + } + catch (Exception ex) + { + _currentAdornerWindow.Capture = false; + if (ex != CheckoutException.Canceled) + { + _currentAdornerWindow._behaviorService.ShowError(ex); + } + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + finally + { + _currentAdornerWindow = null; + } + } + } + + Debug.Assert(_isHooked, "How did we get here when we are diposed?"); + return UnsafeNativeMethods.CallNextHookEx(new HandleRef(this, _mouseHookHandle), nCode, wparam, lparam); + } + + private void UnhookMouse() + { + lock (this) + { + if (_mouseHookHandle != IntPtr.Zero) + { + UnsafeNativeMethods.UnhookWindowsHookEx(new HandleRef(this, _mouseHookHandle)); + _mouseHookRoot.Free(); + _mouseHookHandle = IntPtr.Zero; + _isHooked = false; + } + } + } + + private bool ProcessMouseMessage(IntPtr hWnd, int msg, int x, int y) + { + if (_processingMessage) + { + return false; + } + // We could have hooked a control in a semitrust web page. This would put semitrust frames above us, which could cause this to fail. + // SECREVIEW, UNDONE. Think hard about this. Does this allow a project to have a web page that pointed to a malicious control? + // I don't think so, because the malicious control would still be on the frame. + new NamedPermissionSet("FullTrust").Assert(); + + foreach (AdornerWindow adornerWindow in AdornerWindow.s_adornerWindowList) + { + if (!adornerWindow.DesignerFrameValid) + { + continue; + } + + _currentAdornerWindow = adornerWindow; + IntPtr handle = adornerWindow.DesignerFrame.Handle; + + // if it's us or one of our children, just process as normal + if (adornerWindow.ProcessingDrag || (hWnd != handle && SafeNativeMethods.IsChild(new HandleRef(this, handle), new HandleRef(this, hWnd)))) + { + Debug.Assert(_thisProcessID != 0, "Didn't get our process id!"); + // make sure the window is in our process + UnsafeNativeMethods.GetWindowThreadProcessId(new HandleRef(null, hWnd), out int pid); + // if this isn't our process, bail + if (pid != _thisProcessID) + { + return false; + } + + try + { + _processingMessage = true; + NativeMethods.POINT pt = new NativeMethods.POINT + { + x = x, + y = y + }; + NativeMethods.MapWindowPoints(IntPtr.Zero, adornerWindow.Handle, pt, 1); + Message m = Message.Create(hWnd, msg, (IntPtr)0, (IntPtr)MAKELONG(pt.y, pt.x)); + + // DevDiv Bugs 79616, No one knows why we get an extra click here from VS. As a workaround, we check the TimeStamp and discard it. + if (m.Msg == NativeMethods.WM_LBUTTONDOWN) + { + _lastLButtonDownTimeStamp = UnsafeNativeMethods.GetMessageTime(); + } + else if (m.Msg == NativeMethods.WM_LBUTTONDBLCLK) + { + int lButtonDoubleClickTimeStamp = UnsafeNativeMethods.GetMessageTime(); + if (lButtonDoubleClickTimeStamp == _lastLButtonDownTimeStamp) + { + return true; + } + } + + if (!adornerWindow.WndProcProxy(ref m, pt.x, pt.y)) + { + // we did the work, stop the message propogation + return true; + } + + } + finally + { + _processingMessage = false; + } + break; // no need to enumerate the other adorner windows since only one can be focused at a time. + } + } + return false; + } + + public static int MAKELONG(int low, int high) + { + return (high << 16) | (low & 0xffff); + } + } + } + + private bool PropagateHitTest(Point pt) + { + for (int i = _adorners.Count - 1; i >= 0; i--) + { + if (!_adorners[i].Enabled) + { + continue; + } + + for (int j = 0; j < _adorners[i].Glyphs.Count; j++) + { + Cursor hitTestCursor = _adorners[i].Glyphs[j].GetHitTest(pt); + if (hitTestCursor != null) + { + // InvokeMouseEnterGlyph will cause the selection to change, which might change the number of glyphs, so we need to remember the new glyph before calling InvokeMouseEnterLeave. VSWhidbey #396611 + Glyph newGlyph = _adorners[i].Glyphs[j]; + + //with a valid hit test, fire enter/leave events + InvokeMouseEnterLeave(_hitTestedGlyph, newGlyph); + if (_validDragArgs == null) + { + //if we're not dragging, set the appropriate cursor + SetAppropriateCursor(hitTestCursor); + } + + _hitTestedGlyph = newGlyph; + //return true if we hit on a transparentBehavior, otherwise false + return (_hitTestedGlyph.Behavior is ControlDesigner.TransparentBehavior); + } + } + } + + InvokeMouseEnterLeave(_hitTestedGlyph, null); + if (_validDragArgs == null) + { + Cursor cursor = Cursors.Default; + if ((_behaviorStack != null) && (_behaviorStack.Count > 0)) + { + if (_behaviorStack[0] is Behavior behavior) + { + cursor = behavior.Cursor; + } + } + SetAppropriateCursor(cursor); + } + _hitTestedGlyph = null; + return true; // Returning false will cause the transparent window to return HTCLIENT when handling WM_NCHITTEST, thus blocking underline window to receive mouse events. + } + + private class MenuCommandHandler : IMenuCommandService + { + private readonly BehaviorService _owner; // ptr back to the behavior service + private readonly IMenuCommandService _menuService; // core service used for most implementations of the IMCS interface + private readonly Stack _currentCommands = new Stack(); + + public MenuCommandHandler(BehaviorService owner, IMenuCommandService menuService) + { + _owner = owner; + _menuService = menuService; + } + + public IMenuCommandService MenuService + { + get => _menuService; + } + + void IMenuCommandService.AddCommand(MenuCommand command) + { + _menuService.AddCommand(command); + } + + void IMenuCommandService.RemoveVerb(DesignerVerb verb) + { + _menuService.RemoveVerb(verb); + } + + void IMenuCommandService.RemoveCommand(MenuCommand command) + { + _menuService.RemoveCommand(command); + } + + MenuCommand IMenuCommandService.FindCommand(CommandID commandID) + { + try + { + if (_currentCommands.Contains(commandID)) + { + return null; + } + _currentCommands.Push(commandID); + return _owner.FindCommand(commandID, _menuService); + } + finally + { + _currentCommands.Pop(); + } + } + + bool IMenuCommandService.GlobalInvoke(CommandID commandID) + { + return _menuService.GlobalInvoke(commandID); + } + + void IMenuCommandService.ShowContextMenu(CommandID menuID, int x, int y) + { + _menuService.ShowContextMenu(menuID, x, y); + } + + void IMenuCommandService.AddVerb(DesignerVerb verb) + { + _menuService.AddVerb(verb); + } + + DesignerVerbCollection IMenuCommandService.Verbs + { + get => _menuService.Verbs; + } + } + + private MenuCommand FindCommand(CommandID commandID, IMenuCommandService menuService) + { + Behavior behavior = GetAppropriateBehavior(_hitTestedGlyph); + if (behavior != null) + { + //if the behavior wants all commands disabled.. + if (behavior.DisableAllCommands) + { + MenuCommand menuCommand = menuService.FindCommand(commandID); + if (menuCommand != null) + { + menuCommand.Enabled = false; + } + return menuCommand; + } + // check to see if the behavior wants to interrupt this command + else + { + MenuCommand menuCommand = behavior.FindCommand(commandID); + if (menuCommand != null) + { + // the behavior chose to interrupt - so return the new command + return menuCommand; + } + } + } + return menuService.FindCommand(commandID); + } + + private Behavior GetAppropriateBehavior(Glyph g) { + if (_behaviorStack != null && _behaviorStack.Count > 0) { + return _behaviorStack[0] as Behavior; + } + + if (g != null && g.Behavior != null) { + return g.Behavior; + } + + return null; + } + + private void ShowError(Exception ex) + { + if (_serviceProvider.GetService(typeof(IUIService)) is IUIService uis) + { + uis.ShowError(ex); + } + } + + private void SetAppropriateCursor(Cursor cursor) + { + //default cursors will let the toolbox svc set a cursor if needed + if (cursor == Cursors.Default) + { + if (_toolboxSvc == null) + { + _toolboxSvc = (IToolboxService)_serviceProvider.GetService(typeof(IToolboxService)); + } + + if (_toolboxSvc != null && _toolboxSvc.SetCursor()) + { + cursor = new Cursor(NativeMethods.GetCursor()); + } + } + _adornerWindow.Cursor = cursor; + } + + private void InvokeMouseEnterLeave(Glyph leaveGlyph, Glyph enterGlyph) + { + if (leaveGlyph != null) + { + if (enterGlyph != null && leaveGlyph.Equals(enterGlyph)) + { + //same glyph - no change + return; + } + if (_validDragArgs != null) + { + OnDragLeave(leaveGlyph, EventArgs.Empty); + } + else + { + OnMouseLeave(leaveGlyph); + } + } + + if (enterGlyph != null) + { + if (_validDragArgs != null) + { + OnDragEnter(enterGlyph, _validDragArgs); + } + else + { + OnMouseEnter(enterGlyph); + } + } + } + private void OnDragEnter(Glyph g, DragEventArgs e) + { + // if the AdornerWindow receives a drag message, this fn() will be called w/o a glyph - so we'll assign the last hit tested one + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "BS::OnDragEnter"); + if (g == null) + { + g = _hitTestedGlyph; + } + + Behavior behavior = GetAppropriateBehavior(g); + if (behavior == null) + { + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "\tNo behavior, returning"); + return; + } + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "\tForwarding to behavior"); + behavior.OnDragEnter(g, e); + + if (g != null && g is ControlBodyGlyph && e.Effect == DragDropEffects.None) + { + _dragEnterReplies[g] = this; // dummy value, we just need to set something. + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "\tCalled DragEnter on this glyph. Caching"); + } + } + + private void OnDragLeave(Glyph g, EventArgs e) + { + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "BS::DragLeave"); + // This is normally cleared on OnMouseUp, but we might not get an OnMouseUp to clear it. VSWhidbey #474259 + // So let's make sure it is really cleared when we start the drag. + _dragEnterReplies.Clear(); + + // if the AdornerWindow receives a drag message, this fn() will be called w/o a glyph - so we'll assign the last hit tested one + if (g == null) + { + g = _hitTestedGlyph; + } + + Behavior behavior = GetAppropriateBehavior(g); + if (behavior == null) + { + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "\t No behavior returning "); + return; + } + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "\tBehavior found calling OnDragLeave"); + behavior.OnDragLeave(g, e); + } + + private bool OnMouseDoubleClick(MouseButtons button, Point mouseLoc) + { + Behavior behavior = GetAppropriateBehavior(_hitTestedGlyph); + if (behavior == null) + { + return false; + } + return behavior.OnMouseDoubleClick(_hitTestedGlyph, button, mouseLoc); + } + + private bool OnMouseDown(MouseButtons button, Point mouseLoc) + { + Behavior behavior = GetAppropriateBehavior(_hitTestedGlyph); + if (behavior == null) + { + return false; + } + return behavior.OnMouseDown(_hitTestedGlyph, button, mouseLoc); + } + + private bool OnMouseEnter(Glyph g) + { + Behavior behavior = GetAppropriateBehavior(g); + if (behavior == null) + { + return false; + } + return behavior.OnMouseEnter(g); + } + + private bool OnMouseHover(Point mouseLoc) + { + Behavior behavior = GetAppropriateBehavior(_hitTestedGlyph); + if (behavior == null) + { + return false; + } + return behavior.OnMouseHover(_hitTestedGlyph, mouseLoc); + } + + private bool OnMouseLeave(Glyph g) + { + //stop tracking mouse events for MouseHover + UnHookMouseEvent(); + + Behavior behavior = GetAppropriateBehavior(g); + if (behavior == null) + { + return false; + } + return behavior.OnMouseLeave(g); + } + + private bool OnMouseMove(MouseButtons button, Point mouseLoc) + { + //hook mouse events (if we haven't already) for MouseHover + HookMouseEvent(); + + Behavior behavior = GetAppropriateBehavior(_hitTestedGlyph); + if (behavior == null) + { + return false; + } + return behavior.OnMouseMove(_hitTestedGlyph, button, mouseLoc); + } + + private bool OnMouseUp(MouseButtons button) + { + _dragEnterReplies.Clear(); + _validDragArgs = null; + Behavior behavior = GetAppropriateBehavior(_hitTestedGlyph); + if (behavior == null) + { + return false; + } + return behavior.OnMouseUp(_hitTestedGlyph, button); + } + private void HookMouseEvent() + { + if (!_trackingMouseEvent) + { + _trackingMouseEvent = true; + if (_trackMouseEvent == null) + { + _trackMouseEvent = new NativeMethods.TRACKMOUSEEVENT + { + dwFlags = NativeMethods.TME_HOVER, + hwndTrack = _adornerWindow.Handle + }; + } + SafeNativeMethods.TrackMouseEvent(_trackMouseEvent); + } + } + private void UnHookMouseEvent() + { + _trackingMouseEvent = false; + } + + private void OnDragDrop(DragEventArgs e) + { + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "BS::OnDragDrop"); + _validDragArgs = null;//be sure to null out our cached drag args + Behavior behavior = GetAppropriateBehavior(_hitTestedGlyph); + if (behavior == null) + { + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "\tNo behavior. returning"); + return; + } + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "\tForwarding to behavior"); + behavior.OnDragDrop(_hitTestedGlyph, e); + } + private void PropagatePaint(PaintEventArgs pe) + { + for (int i = 0; i < _adorners.Count; i++) + { + if (!_adorners[i].Enabled) + { + continue; + } + for (int j = _adorners[i].Glyphs.Count - 1; j >= 0; j--) + { + _adorners[i].Glyphs[j].Paint(pe); + } + } + } + + [SuppressMessage("Microsoft.Performance", "CA1818:DoNotConcatenateStringsInsideLoops")] + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + private void TestHook_GetRecentSnapLines(ref Message m) + { + string snapLineInfo = ""; + if (_testHook_RecentSnapLines != null) + { + foreach (string line in _testHook_RecentSnapLines) + { + snapLineInfo += line + "\n"; + } + } + TestHook_SetText(ref m, snapLineInfo); + } + + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] + private void TestHook_SetText(ref Message m, string text) + { + if (m.LParam == IntPtr.Zero) + { + m.Result = (IntPtr)((text.Length + 1) * Marshal.SystemDefaultCharSize); + return; + } + + if (unchecked((int)(long)m.WParam) < text.Length + 1) + { + m.Result = (IntPtr)(-1); + return; + } + + // Copy the name into the given IntPtr + char[] nullChar = new char[] { (char)0 }; + byte[] nullBytes; + byte[] bytes; + + if (Marshal.SystemDefaultCharSize == 1) + { + bytes = System.Text.Encoding.Default.GetBytes(text); + nullBytes = System.Text.Encoding.Default.GetBytes(nullChar); + } + else + { + bytes = System.Text.Encoding.Unicode.GetBytes(text); + nullBytes = System.Text.Encoding.Unicode.GetBytes(nullChar); + } + + Marshal.Copy(bytes, 0, m.LParam, bytes.Length); + Marshal.Copy(nullBytes, 0, unchecked((IntPtr)((long)m.LParam + (long)bytes.Length)), nullBytes.Length); + m.Result = (IntPtr)((bytes.Length + nullBytes.Length) / Marshal.SystemDefaultCharSize); + } + + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + private void TestHook_GetAllSnapLines(ref Message m) + { + string snapLineInfo = ""; + if (!(_serviceProvider.GetService(typeof(IDesignerHost)) is IDesignerHost host)) + { + return; + } + + foreach (Component comp in host.Container.Components) + { + if (!(comp is Control)) + { + continue; + } + + if (host.GetDesigner(comp) is ControlDesigner designer) + { + foreach (SnapLine line in designer.SnapLines) + { + snapLineInfo += line.ToString() + "\tAssociated Control = " + designer.Control.Name + ":::"; + } + } + } + TestHook_SetText(ref m, snapLineInfo); + } + private void OnDragOver(DragEventArgs e) + { + // cache off our validDragArgs so we can re-fabricate enter/leave drag events + _validDragArgs = e; + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "BS::DragOver"); + Behavior behavior = GetAppropriateBehavior(_hitTestedGlyph); + if (behavior == null) + { + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "\tNo behavior, exiting with DragDropEffects.None"); + e.Effect = DragDropEffects.None; + return; + } + if (_hitTestedGlyph == null || + (_hitTestedGlyph != null && !_dragEnterReplies.ContainsKey(_hitTestedGlyph))) + { + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "\tFound glyph, forwarding to behavior"); + behavior.OnDragOver(_hitTestedGlyph, e); + } + else + { + Debug.WriteLineIf(s_dragDropSwitch.TraceVerbose, "\tFell through"); + e.Effect = DragDropEffects.None; + } + } + + private void OnGiveFeedback(GiveFeedbackEventArgs e) + { + Behavior behavior = GetAppropriateBehavior(_hitTestedGlyph); + if (behavior == null) + { + return; + } + behavior.OnGiveFeedback(_hitTestedGlyph, e); + } + + private void OnQueryContinueDrag(QueryContinueDragEventArgs e) + { + Behavior behavior = GetAppropriateBehavior(_hitTestedGlyph); + if (behavior == null) + { + return; + } + behavior.OnQueryContinueDrag(_hitTestedGlyph, e); + } + + private void OnSystemSettingChanged(object sender, EventArgs e) + { + SyncSelection(); + DesignerUtils.SyncBrushes(); + } + + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + SyncSelection(); + DesignerUtils.SyncBrushes(); } } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorBehavior.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorBehavior.cs new file mode 100644 index 00000000000..4fb6b7b609e --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorBehavior.cs @@ -0,0 +1,247 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// This behavior is associated with the ContainerGlyph offered up by ParentControlDesigner. This Behavior simply starts a new dragdrop behavior. + /// + internal sealed class ContainerSelectorBehavior : Behavior + { + private Control _containerControl; //our related control + private IServiceProvider _serviceProvider; //used for starting a drag/drop + private BehaviorService _behaviorService; //ptr to where we start our drag/drop operation + private bool _okToMove; //state identifying if we are allowed to move the container + private Point _initialDragPoint; //cached "mouse down" point + + // For some controls, we want to change the original drag point to be the upper-left of the control in order to make it easier to drop the control at a desired location. But not all controls want this behavior. E.g. we want to do it for Panel and ToolStrip, but not for Label. Label has a ContainerSelectorBehavior via the NoResizeSelectionBorder glyph. + private readonly bool _setInitialDragPoint; + + /// + /// Constructor, here we cache off all of our member vars and sync location & size changes. + /// + internal ContainerSelectorBehavior(Control containerControl, IServiceProvider serviceProvider) + { + Init(containerControl, serviceProvider); + _setInitialDragPoint = false; + } + + /// + /// Constructor, here we cache off all of our member vars and sync location & size changes. + /// + internal ContainerSelectorBehavior(Control containerControl, IServiceProvider serviceProvider, bool setInitialDragPoint) + { + Init(containerControl, serviceProvider); + _setInitialDragPoint = setInitialDragPoint; + } + + private void Init(Control containerControl, IServiceProvider serviceProvider) + { + _behaviorService = (BehaviorService)serviceProvider.GetService(typeof(BehaviorService)); + if (_behaviorService == null) + { + Debug.Fail("Could not get the BehaviorService from ContainerSelectroBehavior!"); + return; + } + + _containerControl = containerControl; + _serviceProvider = serviceProvider; + _initialDragPoint = Point.Empty; + _okToMove = false; + } + + + public Control ContainerControl + { + get => _containerControl; + } + + /// + /// This will be true when we detect a mousedown on our glyph. The Glyph can use this state to always return 'true' from hittesting indicating that it would like all messages (like mousemove). + /// + public bool OkToMove + { + get => _okToMove; + set => _okToMove = value; + } + + public Point InitialDragPoint + { + get => _initialDragPoint; + set => _initialDragPoint = value; + } + + /// + /// If the user selects the containerglyph - select our related component. + /// + public override bool OnMouseDown(Glyph g, MouseButtons button, Point mouseLoc) + { + if (button == MouseButtons.Left) + { + //select our component + ISelectionService selSvc = (ISelectionService)_serviceProvider.GetService(typeof(ISelectionService)); + if (selSvc != null && !_containerControl.Equals(selSvc.PrimarySelection as Control)) + { + selSvc.SetSelectedComponents(new object[] { _containerControl }, SelectionTypes.Primary | SelectionTypes.Toggle); + // Setting the selected component will create a new glyph, so this instance of the glyph won't receive any more mouse messages. So we need to tell the new glyph what the initialDragPoint and okToMove are. + if (!(g is ContainerSelectorGlyph selOld)) + { + return false; + } + foreach (Adorner a in _behaviorService.Adorners) + { + foreach (Glyph glyph in a.Glyphs) + { + if (!(glyph is ContainerSelectorGlyph selNew)) + { + continue; + } + // Don't care if we are looking at the same containerselectorglyph + if (selNew.Equals(selOld)) + { + continue; + } + // Check if the containercontrols are the same + if (!(selNew.RelatedBehavior is ContainerSelectorBehavior behNew) || !(selOld.RelatedBehavior is ContainerSelectorBehavior behOld)) + { + continue; + } + + // and the relatedcomponents are the same, then we have found the new glyph that just got added + if (behOld.ContainerControl.Equals(behNew.ContainerControl)) + { + behNew.OkToMove = true; + behNew.InitialDragPoint = DetermineInitialDragPoint(mouseLoc); + break; + } + } + } + } + else + { + InitialDragPoint = DetermineInitialDragPoint(mouseLoc); + //set 'okToMove' to true since the user actually clicked down on the glyph + OkToMove = true; + } + } + return false; + } + + private Point DetermineInitialDragPoint(Point mouseLoc) + { + if (_setInitialDragPoint) + { + // Set the mouse location to be to control's location. + Point controlOrigin = _behaviorService.ControlToAdornerWindow(_containerControl); + controlOrigin = _behaviorService.AdornerWindowPointToScreen(controlOrigin); + Cursor.Position = controlOrigin; + return controlOrigin; + } + else + { + // This really amounts to doing nothing + return mouseLoc; + } + } + + /// + /// We will compare the mouse loc to the initial point (set in onmousedown) and if we're far enough, we'll create a dropsourcebehavior object and start out drag operation! + /// + public override bool OnMouseMove(Glyph g, MouseButtons button, Point mouseLoc) + { + if (button == MouseButtons.Left && OkToMove) + { + if (InitialDragPoint == Point.Empty) + { + InitialDragPoint = DetermineInitialDragPoint(mouseLoc); + } + Size delta = new Size(Math.Abs(mouseLoc.X - InitialDragPoint.X), Math.Abs(mouseLoc.Y - InitialDragPoint.Y)); + if (delta.Width >= DesignerUtils.MinDragSize.Width / 2 || delta.Height >= DesignerUtils.MinDragSize.Height / 2) + { + //start our drag! + Point screenLoc = _behaviorService.AdornerWindowToScreen(); + screenLoc.Offset(mouseLoc.X, mouseLoc.Y); + StartDragOperation(screenLoc); + } + } + return false; + } + + /// + /// Simply clear the initial drag point, so we can start again on the next mouse down. + /// + public override bool OnMouseUp(Glyph g, MouseButtons button) + { + InitialDragPoint = Point.Empty; + OkToMove = false; + return false; + } + + /// + /// Called when we've identified that we want to start a drag operation with our data container. + /// + private void StartDragOperation(Point initialMouseLocation) + { + //need to grab a hold of some services + ISelectionService selSvc = (ISelectionService)_serviceProvider.GetService(typeof(ISelectionService)); + IDesignerHost host = (IDesignerHost)_serviceProvider.GetService(typeof(IDesignerHost)); + if (selSvc == null || host == null) + { + Debug.Fail("Can't drag this Container! Either SelectionService is null or DesignerHost is null"); + return; + } + //must identify a required parent to avoid dragging mixes of children + Control requiredParent = _containerControl.Parent; + ArrayList dragControls = new ArrayList(); + ICollection selComps = selSvc.GetSelectedComponents(); + //create our list of controls-to-drag + foreach (IComponent comp in selComps) + { + if ((comp is Control ctrl) && (ctrl.Parent != null)) + { + if (!ctrl.Parent.Equals(requiredParent)) + { + continue; //mixed selection of different parents - don't add this + } + if (host.GetDesigner(ctrl) is ControlDesigner des && (des.SelectionRules & SelectionRules.Moveable) != 0) + { + dragControls.Add(ctrl); + } + } + } + + //if we have controls-to-drag, create our new behavior and start the drag/drop operation + if (dragControls.Count > 0) + { + Point controlOrigin; + if (_setInitialDragPoint) + { + // In this case we want the initialmouselocation to be the control's origin. + controlOrigin = _behaviorService.ControlToAdornerWindow(_containerControl); + controlOrigin = _behaviorService.AdornerWindowPointToScreen(controlOrigin); + } + else + { + controlOrigin = initialMouseLocation; + } + DropSourceBehavior dsb = new DropSourceBehavior(dragControls, _containerControl.Parent, controlOrigin); + try + { + _behaviorService.DoDragDrop(dsb); + } + finally + { + OkToMove = false; + InitialDragPoint = Point.Empty; + } + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorGlyph.cs new file mode 100644 index 00000000000..9776723833d --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorGlyph.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// This is the glyph used to drag container controls around the designer. This glyph (and associated behavior) is created by the ParentControlDesigner. + /// + internal sealed class ContainerSelectorGlyph : Glyph + { + private Rectangle _glyphBounds; + private readonly ContainerSelectorBehavior _relatedBehavior; + + /// + /// ContainerSelectorGlyph constructor. + /// + internal ContainerSelectorGlyph(Rectangle containerBounds, int glyphSize, int glyphOffset, ContainerSelectorBehavior behavior) : base(behavior) + { + _relatedBehavior = (ContainerSelectorBehavior)behavior; + _glyphBounds = new Rectangle(containerBounds.X + glyphOffset, containerBounds.Y - (int)(glyphSize * .5), glyphSize, glyphSize); + } + + + /// + /// The bounds of this Glyph. + /// + public override Rectangle Bounds + { + get => _glyphBounds; + } + + public Behavior RelatedBehavior + { + get => _relatedBehavior; + } + + /// + /// Simple hit test rule: if the point is contained within the bounds - then it is a positive hit test. + /// + public override Cursor GetHitTest(Point p) + { + if (_glyphBounds.Contains(p) || _relatedBehavior.OkToMove) + { + return Cursors.SizeAll; + } + return null; + } + + private Bitmap _glyph = null; + private Bitmap MoveGlyph + { + get + { + if (_glyph == null) + { + _glyph = new Bitmap(typeof(ContainerSelectorGlyph), "MoverGlyph.bmp"); + _glyph.MakeTransparent(); + } + return _glyph; + } + } + + /// + /// Very simple paint logic. + /// + public override void Paint(PaintEventArgs pe) + { + pe.Graphics.DrawImage(MoveGlyph, _glyphBounds); + } + + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ResizeBehavior.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ResizeBehavior.cs new file mode 100644 index 00000000000..ded22ee43f2 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ResizeBehavior.cs @@ -0,0 +1,899 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// The ResizeBehavior is pushed onto the BehaviorStack in response to a positively hit tested SelectionGlyph. The ResizeBehavior simply tracks the MouseMove messages and updates the bounds of the relatd control based on the new mouse location and the resize Rules. + /// + internal class ResizeBehavior : Behavior + { + private struct ResizeComponent + { + public object resizeControl; + public Rectangle resizeBounds; + public SelectionRules resizeRules; + }; + + private ResizeComponent[] _resizeComponents; + private readonly IServiceProvider _serviceProvider; + private BehaviorService _behaviorService; + private SelectionRules _targetResizeRules; //rules dictating which sizes we can change + private Point _initialPoint; //the initial point of the mouse down + private bool _dragging; //indicates that the behavior is currently 'dragging' + private bool _pushedBehavior; + private bool _initialResize; //true for the first resize of the control, false after that. + private DesignerTransaction _resizeTransaction; //the transaction we create for the resize + private const int MINSIZE = 10; + private const int BorderSize = 2; + private DragAssistanceManager _dragManager; //this object will integrate SnapLines into the resize + private Point _lastMouseLoc; //helps us avoid re-entering code if the mouse hasn't moved + private Point _parentLocation; //used to snap resize ops to the grid + private Size _parentGridSize; //used to snap resize ops to the grid + private NativeMethods.POINT _lastMouseAbs; // last absolute mouse position + private Point _lastSnapOffset; //the last snapoffset we used. + private bool _didSnap; //did we actually snap. + private Control _primaryControl; //the primary control the status bar will queue off of + + private Cursor _cursor = Cursors.Default; //used to set the correct cursor during resizing + private readonly StatusCommandUI _statusCommandUI; // used to update the StatusBar Information. + + private Region _lastResizeRegion; + private bool _captureLost; + + /// + /// Constructor that caches all values for perf. reasons. + /// + internal ResizeBehavior(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + _dragging = false; + _pushedBehavior = false; + _lastSnapOffset = Point.Empty; + _didSnap = false; + _statusCommandUI = new StatusCommandUI(serviceProvider); + } + + /// + /// Demand creates the BehaviorService. + /// + private BehaviorService BehaviorService + { + get + { + if (_behaviorService == null) + { + _behaviorService = (BehaviorService)_serviceProvider.GetService(typeof(BehaviorService)); + } + return _behaviorService; + } + } + + public override Cursor Cursor + { + get + { + return _cursor; + } + } + + /// + /// Called during the resize operation, we'll try to determine an offset so that the controls snap to the grid settings of the parent. + /// + private Rectangle AdjustToGrid(Rectangle controlBounds, SelectionRules rules) + { + + Rectangle rect = controlBounds; + + if ((rules & SelectionRules.RightSizeable) != 0) + { + int xDelta = controlBounds.Right % _parentGridSize.Width; + if (xDelta > _parentGridSize.Width / 2) + { + rect.Width += _parentGridSize.Width - xDelta; + } + else + { + rect.Width -= xDelta; + } + } + else if ((rules & SelectionRules.LeftSizeable) != 0) + { + int xDelta = controlBounds.Left % _parentGridSize.Width; + if (xDelta > _parentGridSize.Width / 2) + { + rect.X += _parentGridSize.Width - xDelta; + rect.Width -= _parentGridSize.Width - xDelta; + } + else + { + rect.X -= xDelta; + rect.Width += xDelta; + } + } + + if ((rules & SelectionRules.BottomSizeable) != 0) + { + int yDelta = controlBounds.Bottom % _parentGridSize.Height; + if (yDelta > _parentGridSize.Height / 2) + { + rect.Height += _parentGridSize.Height - yDelta; + } + else + { + rect.Height -= yDelta; + } + } + else if ((rules & SelectionRules.TopSizeable) != 0) + { + int yDelta = controlBounds.Top % _parentGridSize.Height; + if (yDelta > _parentGridSize.Height / 2) + { + rect.Y += _parentGridSize.Height - yDelta; + rect.Height -= _parentGridSize.Height - yDelta; + } + else + { + rect.Y -= yDelta; + rect.Height += yDelta; + } + } + + //validate our dimensions + rect.Width = Math.Max(rect.Width, _parentGridSize.Width); + rect.Height = Math.Max(rect.Height, _parentGridSize.Height); + + return rect; + } + + /// + /// Builds up an array of snaplines used during resize to adjust/snap the controls bounds. + /// + private SnapLine[] GenerateSnapLines(SelectionRules rules, Point loc) + { + ArrayList lines = new ArrayList(2); + //the four margins and edges of our control + if ((rules & SelectionRules.BottomSizeable) != 0) + { + lines.Add(new SnapLine(SnapLineType.Bottom, loc.Y - 1)); + if (_primaryControl != null) + { + lines.Add(new SnapLine(SnapLineType.Horizontal, loc.Y + _primaryControl.Margin.Bottom, SnapLine.MarginBottom, SnapLinePriority.Always)); + } + } + else if ((rules & SelectionRules.TopSizeable) != 0) + { + lines.Add(new SnapLine(SnapLineType.Top, loc.Y)); + if (_primaryControl != null) + { + lines.Add(new SnapLine(SnapLineType.Horizontal, loc.Y - _primaryControl.Margin.Top, SnapLine.MarginTop, SnapLinePriority.Always)); + } + } + + if ((rules & SelectionRules.RightSizeable) != 0) + { + lines.Add(new SnapLine(SnapLineType.Right, loc.X - 1)); + if (_primaryControl != null) + { + lines.Add(new SnapLine(SnapLineType.Vertical, loc.X + _primaryControl.Margin.Right, SnapLine.MarginRight, SnapLinePriority.Always)); + } + } + else if ((rules & SelectionRules.LeftSizeable) != 0) + { + lines.Add(new SnapLine(SnapLineType.Left, loc.X)); + if (_primaryControl != null) + { + lines.Add(new SnapLine(SnapLineType.Vertical, loc.X - _primaryControl.Margin.Left, SnapLine.MarginLeft, SnapLinePriority.Always)); + } + } + + SnapLine[] l = new SnapLine[lines.Count]; + lines.CopyTo(l); + + return l; + } + + /// + /// This is called in response to the mouse moving far enough away from its initial point. Basically, we calculate the bounds for each control we're resizing and disable any adorners. + /// + private void InitiateResize() + { + bool useSnapLines = BehaviorService.UseSnapLines; + ArrayList components = new ArrayList(); + //check to see if the current designer participate with SnapLines cache the control bounds + for (int i = 0; i < _resizeComponents.Length; i++) + { + _resizeComponents[i].resizeBounds = ((Control)(_resizeComponents[i].resizeControl)).Bounds; + if (useSnapLines) + { + components.Add(_resizeComponents[i].resizeControl); + } + if (_serviceProvider.GetService(typeof(IDesignerHost)) is IDesignerHost designerHost) + { + if (designerHost.GetDesigner(_resizeComponents[i].resizeControl as Component) is ControlDesigner designer) + { + _resizeComponents[i].resizeRules = designer.SelectionRules; + } + else + { + Debug.Fail("Initiating resize. Could not get the designer for " + _resizeComponents[i].resizeControl.ToString()); + _resizeComponents[i].resizeRules = SelectionRules.None; + } + } + } + + //disable all glyphs in all adorners + BehaviorService.EnableAllAdorners(false); + //build up our resize transaction + IDesignerHost host = (IDesignerHost)_serviceProvider.GetService(typeof(IDesignerHost)); + if (host != null) + { + string locString; + if (_resizeComponents.Length == 1) + { + string name = TypeDescriptor.GetComponentName(_resizeComponents[0].resizeControl); + if (name == null || name.Length == 0) + { + name = _resizeComponents[0].resizeControl.GetType().Name; + } + locString = string.Format(SR.BehaviorServiceResizeControl, name); + } + else + { + locString = string.Format(SR.BehaviorServiceResizeControls, _resizeComponents.Length); + } + _resizeTransaction = host.CreateTransaction(locString); + } + + _initialResize = true; + if (useSnapLines) + { + //instantiate our class to manage snap/margin lines... + _dragManager = new DragAssistanceManager(_serviceProvider, components, true); + } + else if (_resizeComponents.Length > 0) + { + //try to get the parents grid and snap settings + if (_resizeComponents[0].resizeControl is Control control && control.Parent != null) + { + PropertyDescriptor snapProp = TypeDescriptor.GetProperties(control.Parent)["SnapToGrid"]; + if (snapProp != null && (bool)snapProp.GetValue(control.Parent)) + { + PropertyDescriptor gridProp = TypeDescriptor.GetProperties(control.Parent)["GridSize"]; + if (gridProp != null) + { + //cache of the gridsize and the location of the parent on the adornerwindow + _parentGridSize = (Size)gridProp.GetValue(control.Parent); + _parentLocation = _behaviorService.ControlToAdornerWindow(control); + _parentLocation.X -= control.Location.X; + _parentLocation.Y -= control.Location.Y; + } + } + } + } + _captureLost = false; + } + + /// + /// In response to a MouseDown, the SelectionBehavior will push (initiate) a dragBehavior by alerting the SelectionMananger that a new control has been selected and the mouse is down. Note that this is only if we find the related control's Dock property == none. + /// + public override bool OnMouseDown(Glyph g, MouseButtons button, Point mouseLoc) + { + //we only care about the right mouse button for resizing + if (button != MouseButtons.Left) + { + //pass any other mouse click along - unless we've already started our resize in which case we'll ignore it + return _pushedBehavior; + } + //start with no selection rules and try to obtain this info from the glyph + _targetResizeRules = SelectionRules.None; + if (g is SelectionGlyphBase sgb) + { + _targetResizeRules = sgb.SelectionRules; + _cursor = sgb.HitTestCursor; + } + + if (_targetResizeRules == SelectionRules.None) + { + return false; + } + + ISelectionService selSvc = (ISelectionService)_serviceProvider.GetService(typeof(ISelectionService)); + if (selSvc == null) + { + return false; + } + + _initialPoint = mouseLoc; + _lastMouseLoc = mouseLoc; + //build up a list of our selected controls + _primaryControl = selSvc.PrimarySelection as Control; + + // Since we don't know exactly how many valid objects we are going to have we use this temp + ArrayList components = new ArrayList(); + foreach (object o in selSvc.GetSelectedComponents()) + { + if (o is Control) + { + //don't drag locked controls + PropertyDescriptor prop = TypeDescriptor.GetProperties(o)["Locked"]; + if (prop != null) + { + if ((bool)prop.GetValue(o)) + continue; + } + components.Add(o); + } + } + + if (components.Count == 0) + { + return false; + } + + _resizeComponents = new ResizeComponent[components.Count]; + for (int i = 0; i < components.Count; i++) + { + _resizeComponents[i].resizeControl = components[i]; + } + + //push this resizebehavior + _pushedBehavior = true; + BehaviorService.PushCaptureBehavior(this); + return false; + } + + /// + /// This method is called when we lose capture, which can occur when another window requests capture or the user presses ESC during a drag. We check to see if we are currently dragging, and if we are we abort the transaction. We pop our behavior off the stack at this time. + /// + public override void OnLoseCapture(Glyph g, EventArgs e) + { + _captureLost = true; + if (_pushedBehavior) + { + _pushedBehavior = false; + Debug.Assert(BehaviorService != null, "We should have a behavior service."); + if (BehaviorService != null) + { + if (_dragging) + { + _dragging = false; + //make sure we get rid of the selection rectangle + for (int i = 0; !_captureLost && i < _resizeComponents.Length; i++) + { + Control control = _resizeComponents[i].resizeControl as Control; + Rectangle borderRect = BehaviorService.ControlRectInAdornerWindow(control); + if (!borderRect.IsEmpty) + { + using (Graphics graphics = BehaviorService.AdornerWindowGraphics) + { + graphics.SetClip(borderRect); + using (Region newRegion = new Region(borderRect)) + { + newRegion.Exclude(Rectangle.Inflate(borderRect, -BorderSize, -BorderSize)); + BehaviorService.Invalidate(newRegion); + } + graphics.ResetClip(); + } + } + } + //re-enable all glyphs in all adorners + BehaviorService.EnableAllAdorners(true); + } + BehaviorService.PopBehavior(this); + + if (_lastResizeRegion != null) + { + BehaviorService.Invalidate(_lastResizeRegion); //might be the same, might not. + _lastResizeRegion.Dispose(); + _lastResizeRegion = null; + } + } + } + + Debug.Assert(!_dragging, "How can we be dragging without pushing a behavior?"); + // If we still have a transaction, roll it back. + if (_resizeTransaction != null) + { + DesignerTransaction t = _resizeTransaction; + _resizeTransaction = null; + using (t) + { + t.Cancel(); + } + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] + internal static int AdjustPixelsForIntegralHeight(Control control, int pixelsMoved) + { + PropertyDescriptor propIntegralHeight = TypeDescriptor.GetProperties(control)["IntegralHeight"]; + if (propIntegralHeight != null) + { + object value = propIntegralHeight.GetValue(control); + if (value is bool && (bool)value == true) + { + PropertyDescriptor propItemHeight = TypeDescriptor.GetProperties(control)["ItemHeight"]; + if (propItemHeight != null) + { + if (pixelsMoved >= 0) + { + return pixelsMoved - (pixelsMoved % (int)propItemHeight.GetValue(control)); + } + else + { + int integralHeight = (int)propItemHeight.GetValue(control); + return pixelsMoved - (integralHeight - (Math.Abs(pixelsMoved) % integralHeight)); + } + } + } + } + //if the control does not have the IntegralHeight property, then the pixels moved are fine + return pixelsMoved; + } + + /// + /// This method will either initiate a new resize operation or continue with an existing one. If we're currently dragging (i.e. resizing) then we look at the resize rules and set the bounds of each control to the new location of the mouse pointer. + /// + public override bool OnMouseMove(Glyph g, MouseButtons button, Point mouseLoc) + { + if (!_pushedBehavior) + { + return false; + } + + bool altKeyPressed = Control.ModifierKeys == Keys.Alt; + if (altKeyPressed && _dragManager != null) + { + //erase any snaplines (if we had any) + _dragManager.EraseSnapLines(); + } + + if (!altKeyPressed && mouseLoc.Equals(_lastMouseLoc)) + { + return true; + } + + // When DesignerWindowPane has scrollbars and we resize, shrinking the the DesignerWindowPane makes it look like the mouse has moved to the BS. To compensate for that we keep track of the mouse's previous position in screen coordinates, and use that to compare if the mouse has really moved. + if (_lastMouseAbs != null) + { + NativeMethods.POINT mouseLocAbs = new NativeMethods.POINT(mouseLoc.X, mouseLoc.Y); + UnsafeNativeMethods.ClientToScreen(new HandleRef(this, _behaviorService.AdornerWindowControl.Handle), mouseLocAbs); + if (mouseLocAbs.x == _lastMouseAbs.x && mouseLocAbs.y == _lastMouseAbs.y) + { + return true; + } + } + + + if (!_dragging) + { + if (Math.Abs(_initialPoint.X - mouseLoc.X) > DesignerUtils.MinDragSize.Width / 2 || Math.Abs(_initialPoint.Y - mouseLoc.Y) > DesignerUtils.MinDragSize.Height / 2) + { + InitiateResize(); + _dragging = true; + } + else + { + return false; + } + } + + if (_resizeComponents == null || _resizeComponents.Length == 0) + { + return false; + } + // we do these separately so as not to disturb the cached sizes for values we're not actually changing. For example, if a control is docked top and we modify the height, the width shouldn't be modified. + PropertyDescriptor propWidth = null; + PropertyDescriptor propHeight = null; + PropertyDescriptor propTop = null; + PropertyDescriptor propLeft = null; + + // We do this to make sure that Undo works correctly. + if (_initialResize) + { + propWidth = TypeDescriptor.GetProperties(_resizeComponents[0].resizeControl)["Width"]; + propHeight = TypeDescriptor.GetProperties(_resizeComponents[0].resizeControl)["Height"]; + propTop = TypeDescriptor.GetProperties(_resizeComponents[0].resizeControl)["Top"]; + propLeft = TypeDescriptor.GetProperties(_resizeComponents[0].resizeControl)["Left"]; + + // validate each of the property descriptors. + if (propWidth != null && !typeof(int).IsAssignableFrom(propWidth.PropertyType)) + { + propWidth = null; + } + + if (propHeight != null && !typeof(int).IsAssignableFrom(propHeight.PropertyType)) + { + propHeight = null; + } + + if (propTop != null && !typeof(int).IsAssignableFrom(propTop.PropertyType)) + { + propTop = null; + } + + if (propLeft != null && !typeof(int).IsAssignableFrom(propLeft.PropertyType)) + { + propLeft = null; + } + } + + Control targetControl = _resizeComponents[0].resizeControl as Control; + _lastMouseLoc = mouseLoc; + _lastMouseAbs = new NativeMethods.POINT(mouseLoc.X, mouseLoc.Y); + UnsafeNativeMethods.ClientToScreen(new HandleRef(this, _behaviorService.AdornerWindowControl.Handle), _lastMouseAbs); + int minHeight = Math.Max(targetControl.MinimumSize.Height, MINSIZE); + int minWidth = Math.Max(targetControl.MinimumSize.Width, MINSIZE); + if (_dragManager != null) + { + bool shouldSnap = true; + bool shouldSnapHorizontally = true; + //if the targetcontrol is at min-size then we do not want to offer up snaplines + if ((((_targetResizeRules & SelectionRules.BottomSizeable) != 0) || ((_targetResizeRules & SelectionRules.TopSizeable) != 0)) && + (targetControl.Height == minHeight)) + { + shouldSnap = false; + } + else if ((((_targetResizeRules & SelectionRules.RightSizeable) != 0) || ((_targetResizeRules & SelectionRules.LeftSizeable) != 0)) && + (targetControl.Width == minWidth)) + { + shouldSnap = false; + } + + //if the targetControl has IntegralHeight turned on, then don't snap if the control can be resized vertically + PropertyDescriptor propIntegralHeight = TypeDescriptor.GetProperties(targetControl)["IntegralHeight"]; + if (propIntegralHeight != null) + { + object value = propIntegralHeight.GetValue(targetControl); + if (value is bool && (bool)value == true) + { + shouldSnapHorizontally = false; + } + } + + if (!altKeyPressed && shouldSnap) + { + //here, ask the snapline engine to suggest an offset during our resize + // Remembering the last snapoffset allows us to correctly erase snaplines, if the user subsequently holds down the Alt-Key. Remember that we don't physically move the mouse, we move the control. So if we didn't remember the last snapoffset and the user then hit the Alt-Key, we would actually redraw the control at the actual mouse location, which would make the control "jump" which is not what the user would expect. Why does the control "jump"? Because when a control is snapped, we have offset the control relative to where the mouse is, but we have not update the physical mouse position. + // When the user hits the Alt-Key they expect the control to be where it was (whether snapped or not). we can't rely on lastSnapOffset to check whether we snapped. We used to check if it was empty, but it can be empty and we still snapped (say the control was snapped, as you continue to move the mouse, it will stay snapped for a while. During that while the snapoffset will got from x to -x (or vice versa) and a one point hit 0. + // Since we have to calculate the new size/location differently based on whether we snapped or not, we have to know for sure if we snapped. We do different math because of bug 264996: + // - if you snap, we want to move the control edge. + // - otherwise, we just want to change the size by the number of pixels moved. + _lastSnapOffset = _dragManager.OnMouseMove(targetControl, GenerateSnapLines(_targetResizeRules, mouseLoc), ref _didSnap, shouldSnapHorizontally); + } + else + { + _dragManager.OnMouseMove(new Rectangle(-100, -100, 0, 0));/*just an invalid rect - so we won't snap*///); + } + + // If there's a line to snap to, the offset will come back non-zero. In that case we should adjust the mouse position with the offset such that the size calculation below takes that offset into account. If there's no line, then the offset is 0, and there's no harm in adding the offset. + mouseLoc.X += _lastSnapOffset.X; + mouseLoc.Y += _lastSnapOffset.Y; + } + + // IF WE ARE SNAPPING TO A CONTROL, then we also need to adjust for the offset between the initialPoint (where the MouseDown happened) and the edge of the control otherwise we would be those pixels off when resizing the control. Remember that snaplines are based on the targetControl, so we need to use the targetControl to figure out the offset. + Rectangle controlBounds = new Rectangle(_resizeComponents[0].resizeBounds.X, _resizeComponents[0].resizeBounds.Y, + _resizeComponents[0].resizeBounds.Width, _resizeComponents[0].resizeBounds.Height); + if ((_didSnap) && (targetControl.Parent != null)) + { + controlBounds.Location = _behaviorService.MapAdornerWindowPoint(targetControl.Parent.Handle, controlBounds.Location); + if (targetControl.Parent.IsMirrored) + { + controlBounds.Offset(-controlBounds.Width, 0); + } + } + + Rectangle newBorderRect = Rectangle.Empty; + Rectangle targetBorderRect = Rectangle.Empty; + bool drawSnapline = true; + Color backColor = targetControl.Parent != null ? targetControl.Parent.BackColor : Color.Empty; + for (int i = 0; i < _resizeComponents.Length; i++) + { + Control control = _resizeComponents[i].resizeControl as Control; + Rectangle bounds = control.Bounds; + Rectangle oldBounds = bounds; + // We need to compute the offset beased on the original cached Bounds ... ListBox doesnt allow drag on the top boundary if this is not done when it is "IntegralHeight" + Rectangle baseBounds = _resizeComponents[i].resizeBounds; + Rectangle oldBorderRect = BehaviorService.ControlRectInAdornerWindow(control); + bool needToUpdate = true; + // The ResizeBehavior can easily get into a situation where we are fighting with a layout engine. E.g., We resize control to 50px, LayoutEngine lays out and finds 50px was too small and resized back to 100px. This is what should happen, but it looks bad in the designer. To avoid the flicker we temporarily turn off painting while we do the resize. + UnsafeNativeMethods.SendMessage(control.Handle, NativeMethods.WM_SETREDRAW, false, /* unused = */ 0); + try + { + bool fRTL = false; + // If the container is mirrored the control origin is in upper-right, so we need to adjust our math for that. Remember that mouse coords have origin in upper left. + if (control.Parent != null && control.Parent.IsMirrored) + { + fRTL = true; + } + // figure out which ones we're actually changing so we don't blow away the controls cached sizing state. This is important if things are docked we don't want to destroy their "pre-dock" size. + BoundsSpecified specified = BoundsSpecified.None; + // When we check if we should change height, width, location, we first have to check if the targetControl allows resizing, and then if the control we are currently resizing allows it as well. + SelectionRules resizeRules = _resizeComponents[i].resizeRules; + if (((_targetResizeRules & SelectionRules.BottomSizeable) != 0) && + ((resizeRules & SelectionRules.BottomSizeable) != 0)) + { + int pixelHeight; + if (_didSnap) + { + pixelHeight = mouseLoc.Y - controlBounds.Bottom; + } + else + { + pixelHeight = AdjustPixelsForIntegralHeight(control, mouseLoc.Y - _initialPoint.Y); + } + + bounds.Height = Math.Max(minHeight, baseBounds.Height + pixelHeight); + specified |= BoundsSpecified.Height; + } + + if (((_targetResizeRules & SelectionRules.TopSizeable) != 0) && + ((resizeRules & SelectionRules.TopSizeable) != 0)) + { + int yOffset; + if (_didSnap) + { + yOffset = controlBounds.Y - mouseLoc.Y; + } + else + { + yOffset = AdjustPixelsForIntegralHeight(control, _initialPoint.Y - mouseLoc.Y); + } + + specified |= BoundsSpecified.Height; + bounds.Height = Math.Max(minHeight, baseBounds.Height + yOffset); + if ((bounds.Height != minHeight) || + ((bounds.Height == minHeight) && (oldBounds.Height != minHeight))) + { + specified |= BoundsSpecified.Y; + //if you do it fast enough, we actually could end up placing the control off the parent (say off the form), so enforce a "minimum" location + bounds.Y = Math.Min(baseBounds.Bottom - minHeight, baseBounds.Y - yOffset); + } + } + + if (((((_targetResizeRules & SelectionRules.RightSizeable) != 0) && ((resizeRules & SelectionRules.RightSizeable) != 0)) && (!fRTL)) || + ((((_targetResizeRules & SelectionRules.LeftSizeable) != 0) && ((resizeRules & SelectionRules.LeftSizeable) != 0)) && (fRTL))) + { + specified |= BoundsSpecified.Width; + int xOffset = _initialPoint.X; + if (_didSnap) + { + xOffset = !fRTL ? controlBounds.Right : controlBounds.Left; + } + bounds.Width = Math.Max(minWidth, baseBounds.Width + (!fRTL ? (mouseLoc.X - xOffset) : (xOffset - mouseLoc.X))); + } + + if (((((_targetResizeRules & SelectionRules.RightSizeable) != 0) && ((resizeRules & SelectionRules.RightSizeable) != 0)) && (fRTL)) || + ((((_targetResizeRules & SelectionRules.LeftSizeable) != 0) && ((resizeRules & SelectionRules.LeftSizeable) != 0)) && (!fRTL))) + { + specified |= BoundsSpecified.Width; + int xPos = _initialPoint.X; + if (_didSnap) + { + xPos = !fRTL ? controlBounds.Left : controlBounds.Right; + } + + int xOffset = !fRTL ? (xPos - mouseLoc.X) : (mouseLoc.X - xPos); + bounds.Width = Math.Max(minWidth, baseBounds.Width + xOffset); + if ((bounds.Width != minWidth) || + ((bounds.Width == minWidth) && (oldBounds.Width != minWidth))) + { + specified |= BoundsSpecified.X; + //if you do it fast enough, we actually could end up placing the control off the parent (say off the form), so enforce a "minimum" location + bounds.X = Math.Min(baseBounds.Right - minWidth, baseBounds.X - xOffset); + } + } + + if (!_parentGridSize.IsEmpty) + { + bounds = AdjustToGrid(bounds, _targetResizeRules); + } + + // Checking specified (check the diff) rather than bounds. != resizeBounds[i]. also handles the following corner cases: + // 1. Create a form and add 2 buttons. Make sure that they are snapped to the left edge. Now grab the left edge of button 1, and start resizing to the left, past the snapline you will initially get, and then back to the right. What you would expect is to get the left edge snapline again. But without the specified check you wouldn't. This is because the bounds. != resizeBounds[i]. checks would fail, since the new size would now be the original size. We could probably live with that, except that we draw the snapline below, since we correctly identified one. We could hack it so that we didn't draw the snapline, but that would confuse the user even more. + // 2. Create a form and add a single button. Place it at 100,100. Now start resizing it to the left and then back to the right. Note that with the original check (see diff), you would never be able to resize it back to position 100,100. You would get to 99,100 and then to 101,100. + if (((specified & BoundsSpecified.Width) == BoundsSpecified.Width) && + _dragging && _initialResize && propWidth != null) + { + propWidth.SetValue(_resizeComponents[i].resizeControl, bounds.Width); + } + + if (((specified & BoundsSpecified.Height) == BoundsSpecified.Height) && + _dragging && _initialResize && propHeight != null) + { + propHeight.SetValue(_resizeComponents[i].resizeControl, bounds.Height); + } + + if (((specified & BoundsSpecified.X) == BoundsSpecified.X) && + _dragging && _initialResize && propLeft != null) + { + propLeft.SetValue(_resizeComponents[i].resizeControl, bounds.X); + } + + if (((specified & BoundsSpecified.Y) == BoundsSpecified.Y) && + _dragging && _initialResize && propTop != null) + { + propTop.SetValue(_resizeComponents[i].resizeControl, bounds.Y); + } + + // We check the dragging bit here at every turn, because if there was a popup we may have lost capture and we are terminated. At that point we shouldn't make any changes. + if (_dragging) + { + control.SetBounds(bounds.X, bounds.Y, bounds.Width, bounds.Height, specified); + //Get the new resize border + newBorderRect = BehaviorService.ControlRectInAdornerWindow(control); + if (control.Equals(targetControl)) + { + Debug.Assert(i == 0, "The first control in the Selection should be the target control"); + targetBorderRect = newBorderRect; + } + + //Check that the control really did resize itself. Some controls (like ListBox, MonthCalendar) might adjust to a slightly different size than the one we pass in SetBounds. If if didn't size, then there's no need to invalidate anything + if (control.Bounds == oldBounds) + { + needToUpdate = false; + } + // We would expect the bounds now to be what we set it to above, but this might not be the case. If the control is hosted with e.g. a FLP, then setting the bounds above actually might force a re-layout, and the control will get moved to another spot. In this case, we don't really want to draw a snapline. Even if we snapped to a snapline, if the control got moved, the snapline would be in the wrong place. + if (control.Bounds != bounds) + { + drawSnapline = false; + } + } + + if (control == _primaryControl && _statusCommandUI != null) + { + _statusCommandUI.SetStatusInformation(control as Component); + } + } + finally + { + // While we were resizing we discarded painting messages to reduce flicker. We now turn painting back on and manually refresh the controls. + UnsafeNativeMethods.SendMessage(control.Handle, NativeMethods.WM_SETREDRAW, true, /* unused = */ 0); + //update the control + if (needToUpdate) + { + Control parent = control.Parent; + if (parent != null) + { + control.Invalidate(/* invalidateChildren = */ true); + parent.Invalidate(oldBounds, /* invalidateChildren = */ true); + parent.Update(); + } + else + { + control.Refresh(); + } + } + + //render the resize border + if (!newBorderRect.IsEmpty) + { + using (Region newRegion = new Region(newBorderRect)) + { + newRegion.Exclude(Rectangle.Inflate(newBorderRect, -BorderSize, -BorderSize)); + //No reason to get smart about only invalidating part of the border. Thought we could be but no.The reason is the order: ... the new border is drawn (last resize) On next mousemove, the control is resized which redraws the control AND ERASES THE BORDER Then we draw the new border - flash baby. Thus this will always flicker. + if (needToUpdate) + { + using (Region oldRegion = new Region(oldBorderRect)) + { + oldRegion.Exclude(Rectangle.Inflate(oldBorderRect, -BorderSize, -BorderSize)); + BehaviorService.Invalidate(oldRegion); + } + } + + //draw the new border captureLost could be true if a popup came up and caused a lose focus + if (!_captureLost) + { + using (Graphics graphics = BehaviorService.AdornerWindowGraphics) + { + if (_lastResizeRegion != null) + { + if (!_lastResizeRegion.Equals(newRegion, graphics)) + { + _lastResizeRegion.Exclude(newRegion); //we don't want to invalidate this region. + BehaviorService.Invalidate(_lastResizeRegion); //might be the same, might not. + _lastResizeRegion.Dispose(); + _lastResizeRegion = null; + } + } + DesignerUtils.DrawResizeBorder(graphics, newRegion, backColor); + } + if (_lastResizeRegion == null) + { + _lastResizeRegion = newRegion.Clone(); //we will need to dispose it later. + } + } + } + } + } + } + + if ((drawSnapline) && (!altKeyPressed) && (_dragManager != null)) + { + _dragManager.RenderSnapLinesInternal(targetBorderRect); + } + + _initialResize = false; + return true; + } + + /// + /// This ends the Behavior by popping itself from the BehaviorStack. Also, all Adorners are re-enabled at the end of a successful drag. + /// + public override bool OnMouseUp(Glyph g, MouseButtons button) + { + try + { + if (_dragging) + { + if (_dragManager != null) + { + _dragManager.OnMouseUp(); + _dragManager = null; + _lastSnapOffset = Point.Empty; + _didSnap = false; + } + + if (_resizeComponents != null && _resizeComponents.Length > 0) + { + // we do these separately so as not to disturb the cached sizes for values we're not actually changing. For example, if a control is docked top and we modify the height, the width shouldn't be modified. + PropertyDescriptor propWidth = TypeDescriptor.GetProperties(_resizeComponents[0].resizeControl)["Width"]; + PropertyDescriptor propHeight = TypeDescriptor.GetProperties(_resizeComponents[0].resizeControl)["Height"]; + PropertyDescriptor propTop = TypeDescriptor.GetProperties(_resizeComponents[0].resizeControl)["Top"]; + PropertyDescriptor propLeft = TypeDescriptor.GetProperties(_resizeComponents[0].resizeControl)["Left"]; + for (int i = 0; i < _resizeComponents.Length; i++) + { + if (propWidth != null && ((Control)_resizeComponents[i].resizeControl).Width != _resizeComponents[i].resizeBounds.Width) + { + propWidth.SetValue(_resizeComponents[i].resizeControl, ((Control)_resizeComponents[i].resizeControl).Width); + } + if (propHeight != null && ((Control)_resizeComponents[i].resizeControl).Height != _resizeComponents[i].resizeBounds.Height) + { + propHeight.SetValue(_resizeComponents[i].resizeControl, ((Control)_resizeComponents[i].resizeControl).Height); + } + + if (propTop != null && ((Control)_resizeComponents[i].resizeControl).Top != _resizeComponents[i].resizeBounds.Y) + { + propTop.SetValue(_resizeComponents[i].resizeControl, ((Control)_resizeComponents[i].resizeControl).Top); + } + if (propLeft != null && ((Control)_resizeComponents[i].resizeControl).Left != _resizeComponents[i].resizeBounds.X) + { + propLeft.SetValue(_resizeComponents[i].resizeControl, ((Control)_resizeComponents[i].resizeControl).Left); + } + + if (_resizeComponents[i].resizeControl == _primaryControl && _statusCommandUI != null) + { + _statusCommandUI.SetStatusInformation(_primaryControl as Component); + } + } + } + } + + if (_resizeTransaction != null) + { + DesignerTransaction t = _resizeTransaction; + _resizeTransaction = null; + using (t) + { + t.Commit(); + } + } + } + finally + { + // This pops us off the stack, re-enables adorners and clears the "dragging" flag. + OnLoseCapture(g, EventArgs.Empty); + } + return false; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs index 1b025ee64be..f6f77241851 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs @@ -2,225 +2,912 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.CodeDom; using System.Collections; +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; +using System.ComponentModel.Design.Serialization; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Drawing; +using System.Drawing.Design; using System.Runtime.InteropServices; using System.Windows.Forms.Design.Behavior; +using Accessibility; namespace System.Windows.Forms.Design { /// - /// - /// Provides a designer that can design components - /// that extend Control. - /// + /// + /// Provides a designer that can design components + /// that extend Control. + /// /// public class ControlDesigner : ComponentDesigner { - protected AccessibleObject accessibilityObj = null; - - protected BehaviorService BehaviorService => throw new NotImplementedException(SR.NotImplementedByDesign); - + protected static readonly Point InvalidPoint = new Point(int.MinValue, int.MinValue); + private static int currentProcessId; + private IDesignerHost host; // the host for our designer + private IDesignerTarget designerTarget; // the target window proc for the control. + + private bool liveRegion; // is the mouse is over a live region of the control? + private bool inHitTest; // A popular way to implement GetHitTest is by WM_NCHITTEST...which would cause a cycle. + private bool hasLocation; // Do we have a location property? + private bool locationChecked; // And did we check it + private bool locked; // signifies if this control is locked or not + private bool initializing; + private bool enabledchangerecursionguard; + + //Behavior work + private BehaviorService behaviorService; //we cache this 'cause we use it so often + private ResizeBehavior resizeBehavior; //the standard behavior for our selection glyphs - demand created + private ContainerSelectorBehavior moveBehavior; //the behavior for non-resize glyphs - demand created + // Services that we use enough to cache + private ISelectionUIService selectionUISvc; + private IEventHandlerService eventSvc; + private IToolboxService toolboxSvc; + private InheritanceUI inheritanceUI; + private IOverlayService overlayService; + // transient values that are used during mouse drags + private Point mouseDragLast = InvalidPoint; // the last position of the mouse during a drag. + private bool mouseDragMoved; // has the mouse been moved during this drag? + private int lastMoveScreenX; + private int lastMoveScreenY; + // Values used to simulate double clicks for controls that don't support them. + private int lastClickMessageTime; + private int lastClickMessagePositionX; + private int lastClickMessagePositionY; + + private Point downPos = Point.Empty; // point used to track first down of a double click + private event EventHandler disposingHandler; + private CollectionChangeEventHandler dataBindingsCollectionChanged; + private Exception thrownException; + + private bool ctrlSelect; // if the CTRL key was down at the mouse down + private bool toolPassThrough; // a tool is selected, allow the parent to draw a rect for it. + private bool removalNotificationHooked = false; + private bool revokeDragDrop = true; + private bool hadDragDrop; + private DesignerControlCollection controls; + private static bool inContextMenu = false; + private DockingActionList dockingAction; + private StatusCommandUI statusCommandUI; // UI for setting the StatusBar Information.. + + private bool forceVisible = true; + private bool autoResizeHandles = false; // used for disabling AutoSize effect on resize modes. Needed for compat. + private Dictionary subclassedChildren; + + protected BehaviorService BehaviorService + { + get + { + if (behaviorService == null) + { + behaviorService = (BehaviorService)GetService(typeof(BehaviorService)); + } + return behaviorService; + } + } internal bool ForceVisible { - get => throw new NotImplementedException(SR.NotImplementedByDesign); - - set => throw new NotImplementedException(SR.NotImplementedByDesign); + get => forceVisible; + set => forceVisible = value; } /// - /// - /// Retrieves a list of associated components. These are components that should be incluced in a cut or copy - /// operation on this component. - /// + /// + /// Retrieves a list of associated components. These are components that should be incluced in a cut or copy + /// operation on this component. + /// /// - public override ICollection AssociatedComponents => - throw new NotImplementedException(SR.NotImplementedByDesign); + public override ICollection AssociatedComponents + { + get + { + ArrayList sitedChildren = null; + foreach (Control c in Control.Controls) + { + if (c.Site != null) + { + if (sitedChildren == null) + { + sitedChildren = new ArrayList(); + } + sitedChildren.Add(c); + } + } + + if (sitedChildren != null) + { + return sitedChildren; + } + return base.AssociatedComponents; + } + } - public virtual AccessibleObject AccessibilityObject => - throw new NotImplementedException(SR.NotImplementedByDesign); + public virtual AccessibleObject AccessibilityObject + { + get + { + if (accessibilityObj == null) + { + accessibilityObj = new ControlDesignerAccessibleObject(this, Control); + } + return accessibilityObj; + } + } /// - /// Retrieves the control we're designing. + /// Retrieves the control we're designing. /// - public virtual Control Control => throw new NotImplementedException(SR.NotImplementedByDesign); + public virtual Control Control + { + get => (Control)Component; + } /// - /// Determines whether drag rects can be drawn on this designer. + /// Determines whether drag rects can be drawn on this designer. /// - protected virtual bool EnableDragRect => throw new NotImplementedException(SR.NotImplementedByDesign); + protected virtual bool EnableDragRect + { + get => false; + } /// - /// Returns the parent component for this control designer. - /// The default implementation just checks to see if the - /// component being designed is a control, and if it is it - /// returns its parent. This property can return null if there - /// is no parent component. + /// Returns the parent component for this control designer. + /// The default implementation just checks to see if the + /// component being designed is a control, and if it is it + /// returns its parent. This property can return null if there + /// is no parent component. /// - protected override IComponent ParentComponent => throw new NotImplementedException(SR.NotImplementedByDesign); + protected override IComponent ParentComponent + { + get + { + if (Component is Control c && c.Parent != null) + { + return c.Parent; + } + return base.ParentComponent; + } + } /// - /// Determines whether or not the ControlDesigner will allow SnapLine alignment during a - /// drag operation when the primary drag control is over this designer, or when a control - /// is being dragged from the toolbox, or when a control is being drawn through click-drag. + /// Determines whether or not the ControlDesigner will allow SnapLine alignment during a + /// drag operation when the primary drag control is over this designer, or when a control + /// is being dragged from the toolbox, or when a control is being drawn through click-drag. /// - public virtual bool ParticipatesWithSnapLines => throw new NotImplementedException(SR.NotImplementedByDesign); + public virtual bool ParticipatesWithSnapLines + { + get => true; + } /// /// public bool AutoResizeHandles { - get => throw new NotImplementedException(SR.NotImplementedByDesign); - set => throw new NotImplementedException(SR.NotImplementedByDesign); + get => autoResizeHandles; + set => autoResizeHandles = value; } /// - /// Retrieves a set of rules concerning the movement capabilities of a component. - /// This should be one or more flags from the SelectionRules class. If no designer - /// provides rules for a component, the component will not get any UI services. + /// Retrieves a set of rules concerning the movement capabilities of a component. + /// This should be one or more flags from the SelectionRules class. If no designer + /// provides rules for a component, the component will not get any UI services. /// - public virtual SelectionRules SelectionRules => throw new NotImplementedException(SR.NotImplementedByDesign); + public virtual SelectionRules SelectionRules + { + get + { + SelectionRules rules = SelectionRules.Visible; + object component = Component; + rules = SelectionRules.Visible; + PropertyDescriptor prop; + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(component); + PropertyDescriptor autoSizeProp = props["AutoSize"]; + PropertyDescriptor autoSizeModeProp = props["AutoSizeMode"]; + if ((prop = props["Location"]) != null && + !prop.IsReadOnly) + { + rules |= SelectionRules.Moveable; + } + + if ((prop = props["Size"]) != null && !prop.IsReadOnly) + { + if (AutoResizeHandles && this.Component != host.RootComponent) + { + rules = IsResizableConsiderAutoSize(autoSizeProp, autoSizeModeProp) ? rules | SelectionRules.AllSizeable : rules; + } + else + { + rules |= SelectionRules.AllSizeable; + } + } + + PropertyDescriptor propDock = props["Dock"]; + if (propDock != null) + { + DockStyle dock = (DockStyle)(int)propDock.GetValue(component); + //gotta adjust if the control's parent is mirrored... this is just such that we add the right resize handles. We need to do it this way, since resize glyphs are added in AdornerWindow coords, and the AdornerWindow is never mirrored. + if (Control.Parent != null && Control.Parent.IsMirrored) + { + if (dock == DockStyle.Left) + { + dock = DockStyle.Right; + } + else if (dock == DockStyle.Right) + { + dock = DockStyle.Left; + } + } + switch (dock) + { + case DockStyle.Top: + rules &= ~(SelectionRules.Moveable | SelectionRules.TopSizeable | SelectionRules.LeftSizeable | SelectionRules.RightSizeable); + break; + case DockStyle.Left: + rules &= ~(SelectionRules.Moveable | SelectionRules.TopSizeable | SelectionRules.LeftSizeable | SelectionRules.BottomSizeable); + break; + case DockStyle.Right: + rules &= ~(SelectionRules.Moveable | SelectionRules.TopSizeable | SelectionRules.BottomSizeable | SelectionRules.RightSizeable); + break; + case DockStyle.Bottom: + rules &= ~(SelectionRules.Moveable | SelectionRules.LeftSizeable | SelectionRules.BottomSizeable | SelectionRules.RightSizeable); + break; + case DockStyle.Fill: + rules &= ~(SelectionRules.Moveable | SelectionRules.TopSizeable | SelectionRules.LeftSizeable | SelectionRules.RightSizeable | SelectionRules.BottomSizeable); + break; + } + } + + PropertyDescriptor pd = props["Locked"]; + if (pd != null) + { + Object value = pd.GetValue(component); + // make sure that value is a boolean, in case someone else added this property + if (value is bool && (bool)value == true) + { + rules = SelectionRules.Locked | SelectionRules.Visible; + } + } + return rules; + } + } + + private bool IsResizableConsiderAutoSize(PropertyDescriptor autoSizeProp, PropertyDescriptor autoSizeModeProp) + { + object component = Component; + bool resizable = true; + bool autoSize = false; + bool growOnly = false; + if (autoSizeProp != null && + !(autoSizeProp.Attributes.Contains(DesignerSerializationVisibilityAttribute.Hidden) || + autoSizeProp.Attributes.Contains(BrowsableAttribute.No))) + { + autoSize = (bool)autoSizeProp.GetValue(component); + } + + if (autoSizeModeProp != null) + { + AutoSizeMode mode = (AutoSizeMode)autoSizeModeProp.GetValue(component); + growOnly = mode == AutoSizeMode.GrowOnly; + } + + if (autoSize) + { + resizable = growOnly; + } + return resizable; + } /// - /// Returns a list of SnapLine objects representing interesting - /// alignment points for this control. These SnapLines are used - /// to assist in the positioning of the control on a parent's - /// surface. + /// Returns a list of SnapLine objects representing interesting + /// alignment points for this control. These SnapLines are used + /// to assist in the positioning of the control on a parent's + /// surface. /// - public virtual IList SnapLines => throw new NotImplementedException(SR.NotImplementedByDesign); + public virtual IList SnapLines + { + get => SnapLinesInternal(); + } + + internal IList SnapLinesInternal() + { + return SnapLinesInternal(Control.Margin); + } - protected override InheritanceAttribute InheritanceAttribute => - throw new NotImplementedException(SR.NotImplementedByDesign); + internal IList SnapLinesInternal(Padding margin) + { + ArrayList snapLines = new ArrayList(4); + int width = Control.Width; // better perf + int height = Control.Height; // better perf + //the four edges of our control + snapLines.Add(new SnapLine(SnapLineType.Top, 0, SnapLinePriority.Low)); + snapLines.Add(new SnapLine(SnapLineType.Bottom, height - 1, SnapLinePriority.Low)); + snapLines.Add(new SnapLine(SnapLineType.Left, 0, SnapLinePriority.Low)); + snapLines.Add(new SnapLine(SnapLineType.Right, width - 1, SnapLinePriority.Low)); + //the four margins of our control + // Even if a control does not have margins, we still want to add Margin snaplines. + // This is because we only try to match to matching snaplines. Makes the code a little easier... + snapLines.Add(new SnapLine(SnapLineType.Horizontal, -margin.Top, SnapLine.MarginTop, SnapLinePriority.Always)); + snapLines.Add(new SnapLine(SnapLineType.Horizontal, margin.Bottom + height, SnapLine.MarginBottom, SnapLinePriority.Always)); + snapLines.Add(new SnapLine(SnapLineType.Vertical, -margin.Left, SnapLine.MarginLeft, SnapLinePriority.Always)); + snapLines.Add(new SnapLine(SnapLineType.Vertical, margin.Right + width, SnapLine.MarginRight, SnapLinePriority.Always)); + return snapLines; + } + + protected override InheritanceAttribute InheritanceAttribute + { + get + { + if (IsRootDesigner) + { + return InheritanceAttribute.Inherited; + } + return base.InheritanceAttribute; + } + } + + internal bool IsRootDesigner + { + get + { + Debug.Assert(this.Component != null, + "this.component needs to be set before this method is valid."); + + bool isRoot = false; + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null && this.Component == host.RootComponent) + { + isRoot = true; + } + return isRoot; + } + } /// - /// Returns the number of internal control designers in the ControlDesigner. An internal control - /// is a control that is not in the IDesignerHost.Container.Components collection. - /// SplitterPanel is an example of one such control. We use this to get SnapLines for the internal - /// control designers. + /// Returns the number of internal control designers in the ControlDesigner. An internal control + /// is a control that is not in the IDesignerHost.Container.Components collection. + /// SplitterPanel is an example of one such control. We use this to get SnapLines for the internal + /// control designers. /// public virtual int NumberOfInternalControlDesigners() { - throw new NotImplementedException(SR.NotImplementedByDesign); + return 0; } /// - /// Returns the internal control designer with the specified index in the ControlDesigner. An internal control - /// is a control that is not in the IDesignerHost.Container.Components collection. - /// SplitterPanel is an example of one such control. - /// internalControlIndex is zero-based. + /// Returns the internal control designer with the specified index in the ControlDesigner. An internal control + /// is a control that is not in the IDesignerHost.Container.Components collection. + /// SplitterPanel is an example of one such control. + /// internalControlIndex is zero-based. /// public virtual ControlDesigner InternalControlDesigner(int internalControlIndex) { - throw new NotImplementedException(SR.NotImplementedByDesign); + return null; } /// - /// Default processing for messages. This method causes the message to - /// get processed by windows, skipping the control. This is useful if - /// you want to block this message from getting to the control, but - /// you do not want to block it from getting to Windows itself because - /// it causes other messages to be generated. + /// Default processing for messages. This method causes the message to + /// get processed by windows, skipping the control. This is useful if + /// you want to block this message from getting to the control, but + /// you do not want to block it from getting to Windows itself because + /// it causes other messages to be generated. /// protected void BaseWndProc(ref Message m) { - throw new NotImplementedException(SR.NotImplementedByDesign); + m.Result = NativeMethods.DefWindowProc(m.HWnd, m.Msg, m.WParam, m.LParam); } /// - /// Determines if the this designer can be parented to the specified desinger -- - /// generally this means if the control for this designer can be parented into the - /// given ParentControlDesigner's designer. + /// Determines if the this designer can be parented to the specified desinger -- + /// generally this means if the control for this designer can be parented into the + /// given ParentControlDesigner's designer. /// public virtual bool CanBeParentedTo(IDesigner parentDesigner) { - ParentControlDesigner p = parentDesigner as ParentControlDesigner; - return p != null && !Control.Contains(p.Control); + return parentDesigner is ParentControlDesigner p && !Control.Contains(p.Control); } /// - /// Default processing for messages. This method causes the message to - /// get processed by the control, rather than the designer. + /// Default processing for messages. This method causes the message to + /// get processed by the control, rather than the designer. /// protected void DefWndProc(ref Message m) { - throw new NotImplementedException(SR.NotImplementedByDesign); + designerTarget.DefWndProc(ref m); } /// - /// Displays the given exception to the user. + /// Displays the given exception to the user. /// protected void DisplayError(Exception e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + IUIService uis = (IUIService)GetService(typeof(IUIService)); + if (uis != null) + { + uis.ShowError(e); + } + else + { + string message = e.Message; + if (message == null || message.Length == 0) + { + message = e.ToString(); + } + RTLAwareMessageBox.Show(Control, message, null, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1, 0); + } } /// - /// Disposes of this object. + /// Disposes of this object. /// protected override void Dispose(bool disposing) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (disposing) + { + if (Control != null) + { + if (dataBindingsCollectionChanged != null) + { + Control.DataBindings.CollectionChanged -= dataBindingsCollectionChanged; + } + + if (Inherited && inheritanceUI != null) + { + inheritanceUI.RemoveInheritedControl(Control); + } + + if (removalNotificationHooked) + { + IComponentChangeService csc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (csc != null) + { + csc.ComponentRemoved -= new ComponentEventHandler(DataSource_ComponentRemoved); + } + removalNotificationHooked = false; + } + disposingHandler?.Invoke(this, EventArgs.Empty); + UnhookChildControls(Control); + } + + if (ContextMenu != null) + { + ContextMenu.Disposed -= new EventHandler(this.DetachContextMenu); + } + + if (designerTarget != null) + { + designerTarget.Dispose(); + } + downPos = Point.Empty; + Control.ControlAdded -= new ControlEventHandler(OnControlAdded); + Control.ControlRemoved -= new ControlEventHandler(OnControlRemoved); + Control.ParentChanged -= new EventHandler(OnParentChanged); + Control.SizeChanged -= new EventHandler(OnSizeChanged); + Control.LocationChanged -= new EventHandler(OnLocationChanged); + Control.EnabledChanged -= new EventHandler(OnEnabledChanged); + } + base.Dispose(disposing); + } + + private void OnControlAdded(object sender, ControlEventArgs e) + { + if (e.Control != null && host != null) + { + if (!(host.GetDesigner(e.Control) is ControlDesigner)) + { + // No, no designer means we must replace the window target in this control. + IWindowTarget oldTarget = e.Control.WindowTarget; + if (!(oldTarget is ChildWindowTarget)) + { + e.Control.WindowTarget = new ChildWindowTarget(this, e.Control, oldTarget); + // Controls added in UserControl.OnLoad() do not setup sniffing WndProc properly. + e.Control.ControlAdded += new ControlEventHandler(OnControlAdded); + } + + // Some controls (primarily RichEdit) will register themselves as drag-drop source/targets when they are instantiated. We have to RevokeDragDrop() for them so that the ParentControlDesigner()'s drag-drop support can work correctly. Normally, the hwnd for the child control is not created at this time, and we will use the WM_CREATE message in ChildWindowTarget's WndProc() to revoke drag-drop. But, if the handle was already created for some reason, we will need to revoke drag-drop right away. + if (e.Control.IsHandleCreated) + { + Application.OleRequired(); + NativeMethods.RevokeDragDrop(e.Control.Handle); + // We only hook the control's children if there was no designer. We leave it up to the designer to hook its own children. + HookChildControls(e.Control); + } + } + } + } + + private interface IDesignerTarget : IDisposable + { + void DefWndProc(ref Message m); + } + + private class ChildWindowTarget : IWindowTarget, IDesignerTarget + { + private ControlDesigner designer; + private Control childControl; + private IWindowTarget oldWindowTarget; + private IntPtr handle = IntPtr.Zero; + + public ChildWindowTarget(ControlDesigner designer, Control childControl, IWindowTarget oldWindowTarget) + { + this.designer = designer; + this.childControl = childControl; + this.oldWindowTarget = oldWindowTarget; + } + + public IWindowTarget OldWindowTarget + { + get + { + return oldWindowTarget; + } + } + + public void DefWndProc(ref Message m) + { + oldWindowTarget.OnMessage(ref m); + } + + [SuppressMessage("Microsoft.Usage", "CA2216:DisposableTypesShouldDeclareFinalizer")] + public void Dispose() + { + // Do nothing. We will pick this up through a null DesignerTarget property when we come out of the message loop. + } + + public void OnHandleChange(IntPtr newHandle) + { + handle = newHandle; + oldWindowTarget.OnHandleChange(newHandle); + } + + public void OnMessage(ref Message m) + { + // If the designer has jumped ship, the continue partying on messages, but send them back to the original control. + if (designer.Component == null) + { + oldWindowTarget.OnMessage(ref m); + return; + } + + // We want these messages to go through the designer's WndProc method, and we want people to be able to do default processing with the designer's DefWndProc. So, we stuff the old window target into the designer's target and then call their WndProc. + IDesignerTarget designerTarget = designer.DesignerTarget; + designer.DesignerTarget = this; + + try + { + designer.WndProc(ref m); + } + catch (Exception ex) + { + designer.SetUnhandledException(childControl, ex); + } + finally + { + + // If the designer disposed us, then we should follow suit. + // + if (designer.DesignerTarget == null) + { + designerTarget.Dispose(); + } + else + { + designer.DesignerTarget = designerTarget; + } + + // Controls (primarily RichEdit) will register themselves as drag-drop source/targets when they are instantiated. Normally, when they are being designed, we will RevokeDragDrop() in their designers. The problem occurs when these controls are inside a UserControl. At that time, we do not have a designer for these controls, and they prevent the ParentControlDesigner's drag-drop from working. What we do is to loop through all child controls that do not have a designer (in HookChildControls()), and RevokeDragDrop() after their handles have been created. + if (m.Msg == NativeMethods.WM_CREATE) + { + Debug.Assert(handle != IntPtr.Zero, "Handle for control not created"); + NativeMethods.RevokeDragDrop(handle); + } + } + } + } + + private void DetachContextMenu(object sender, EventArgs e) + { + ContextMenu = null; + } + + private ContextMenu ContextMenu + { + get => (ContextMenu)ShadowProperties["ContextMenu"]; + set + { + ContextMenu oldValue = (ContextMenu)ShadowProperties["ContextMenu"]; + + if (oldValue != value) + { + EventHandler disposedHandler = new EventHandler(DetachContextMenu); + + if (oldValue != null) + { + oldValue.Disposed -= disposedHandler; + } + + ShadowProperties["ContextMenu"] = value; + + if (value != null) + { + value.Disposed += disposedHandler; + } + } + + } + } + + private void DataSource_ComponentRemoved(object sender, ComponentEventArgs e) + { + // It is possible to use the control designer with NON CONTROl types. + if (Component is Control ctl) + { + Debug.Assert(ctl.DataBindings.Count > 0, "we should not be notified if the control has no dataBindings"); + ctl.DataBindings.CollectionChanged -= dataBindingsCollectionChanged; + for (int i = 0; i < ctl.DataBindings.Count; i++) + { + Binding binding = ctl.DataBindings[i]; + if (binding.DataSource == e.Component) + { + // remove the binding from the control's collection. this will also remove the binding from the bindingManagerBase's bindingscollection + // NOTE: we can't remove the bindingManager from the bindingContext, cause there may be some complex bound controls ( such as the dataGrid, or the ComboBox, or the ListBox ) that still use that bindingManager + ctl.DataBindings.Remove(binding); + } + } + // if after removing those bindings the collection is empty, then unhook the changeNotificationService + if (ctl.DataBindings.Count == 0) + { + IComponentChangeService csc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (csc != null) + { + csc.ComponentRemoved -= new ComponentEventHandler(DataSource_ComponentRemoved); + } + removalNotificationHooked = false; + } + ctl.DataBindings.CollectionChanged += dataBindingsCollectionChanged; + } } /// - /// Enables design time functionality for a child control. The child control is a child - /// of this control designer's control. The child does not directly participate in - /// persistence, but it will if it is exposed as a property of the main control. Consider - /// a control like the SplitContainer: it has two panels, Panel1 and Panel2. These panels - /// are exposed through read only Panel1 and Panel2 properties on the SplitContainer class. - /// SplitContainer's designer calls EnableDesignTime for each panel, which allows other - /// components to be dropped on them. But, in order for the contents of Panel1 and Panel2 - /// to be saved, SplitContainer itself needed to expose the panels as public properties. - /// The child paramter is the control to enable. The name paramter is the name of this - /// control as exposed to the end user. Names need to be unique within a control designer, - /// but do not have to be unique to other control designer's children. - /// This method returns true if the child control could be enabled for design time, or - /// false if the hosting infrastructure does not support it. To support this feature, the - /// hosting infrastructure must expose the INestedContainer class as a service off of the site. + /// Enables design time functionality for a child control. The child control is a child + /// of this control designer's control. The child does not directly participate in + /// persistence, but it will if it is exposed as a property of the main control. Consider + /// a control like the SplitContainer: it has two panels, Panel1 and Panel2. These panels + /// are exposed through read only Panel1 and Panel2 properties on the SplitContainer class. + /// SplitContainer's designer calls EnableDesignTime for each panel, which allows other + /// components to be dropped on them. But, in order for the contents of Panel1 and Panel2 + /// to be saved, SplitContainer itself needed to expose the panels as public properties. + /// The child paramter is the control to enable. The name paramter is the name of this + /// control as exposed to the end user. Names need to be unique within a control designer, + /// but do not have to be unique to other control designer's children. + /// This method returns true if the child control could be enabled for design time, or + /// false if the hosting infrastructure does not support it. To support this feature, the + /// hosting infrastructure must expose the INestedContainer class as a service off of the site. /// protected bool EnableDesignMode(Control child, string name) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (child == null) + { + throw new ArgumentNullException("child"); + } + + if (name == null) + { + throw new ArgumentNullException("name"); + } + + if (!(GetService(typeof(INestedContainer)) is INestedContainer nc)) + { + return false; + } + + // Only add the child if it doesn't already exist. VSWhidbey #408041. + for (int i = 0; i < nc.Components.Count; i++) + { + if (nc.Components[i].Equals(child)) + { + return true; + } + } + nc.Add(child, name); + return true; } /// - /// Enables or disables drag/drop support. This - /// hooks drag event handlers to the control. + /// Enables or disables drag/drop support. This + /// hooks drag event handlers to the control. /// protected void EnableDragDrop(bool value) { - throw new NotImplementedException(SR.NotImplementedByDesign); + Control rc = Control; + if (rc == null) + { + return; + } + + if (value) + { + rc.DragDrop += new DragEventHandler(this.OnDragDrop); + rc.DragOver += new DragEventHandler(this.OnDragOver); + rc.DragEnter += new DragEventHandler(this.OnDragEnter); + rc.DragLeave += new EventHandler(this.OnDragLeave); + rc.GiveFeedback += new GiveFeedbackEventHandler(this.OnGiveFeedback); + hadDragDrop = rc.AllowDrop; + if (!hadDragDrop) + { + rc.AllowDrop = true; + } + revokeDragDrop = false; + } + else + { + rc.DragDrop -= new DragEventHandler(this.OnDragDrop); + rc.DragOver -= new DragEventHandler(this.OnDragOver); + rc.DragEnter -= new DragEventHandler(this.OnDragEnter); + rc.DragLeave -= new EventHandler(this.OnDragLeave); + rc.GiveFeedback -= new GiveFeedbackEventHandler(this.OnGiveFeedback); + if (!hadDragDrop) + { + rc.AllowDrop = false; + } + revokeDragDrop = true; + } } /// - /// Returns a 'BodyGlyph' representing the bounds of this control. - /// The BodyGlyph is responsible for hit testing the related CtrlDes - /// and forwarding messages directly to the designer. + /// Returns a 'BodyGlyph' representing the bounds of this control. + /// The BodyGlyph is responsible for hit testing the related CtrlDes + /// and forwarding messages directly to the designer. /// protected virtual ControlBodyGlyph GetControlGlyph(GlyphSelectionType selectionType) { - throw new NotImplementedException(SR.NotImplementedByDesign); + //get the right cursor for this component + OnSetCursor(); + Cursor cursor = Cursor.Current; + //get the correctly translated bounds + Rectangle translatedBounds = BehaviorService.ControlRectInAdornerWindow(Control); + //create our glyph, and set its cursor appropriately + ControlBodyGlyph g = null; + Control parent = Control.Parent; + if (parent != null && host != null && host.RootComponent != Component) + { + Rectangle parentRect = parent.RectangleToScreen(parent.ClientRectangle); + Rectangle controlRect = Control.RectangleToScreen(Control.ClientRectangle); + if (!parentRect.Contains(controlRect) && !parentRect.IntersectsWith(controlRect)) + { + //since the parent is completely clipping the control, the control cannot be a drop target, and it will not get mouse messages. So we don't have to give the glyph a transparentbehavior (default for ControlBodyGlyph). But we still would like to be able to move the control, so push a MoveBehavior. If we didn't we wouldn't be able to move the control, since it won't get any mouse messages. + + ISelectionService sel = (ISelectionService)GetService(typeof(ISelectionService)); + if (sel != null && sel.GetComponentSelected(Control)) + { + g = new ControlBodyGlyph(translatedBounds, cursor, Control, MoveBehavior); + } + else if (cursor == Cursors.SizeAll) + { + //If we get here, OnSetCursor could have set the cursor to SizeAll. But if we fall into this category, we don't have a MoveBehavior, so we don't want to show the SizeAll cursor. Let's make sure the cursor is set to the default cursor. + cursor = Cursors.Default; + } + } + } + + if (g == null) + { + //we are not totally clipped by the parent + g = new ControlBodyGlyph(translatedBounds, cursor, Control, this); + } + return g; } /// - /// Returns a collection of Glyph objects representing the selection - /// borders and grab handles for a standard control. Note that - /// based on 'selectionType' the Glyphs returned will either: represent - /// a fully resizeable selection border with grab handles, a locked - /// selection border, or a single 'hidden' selection Glyph. + /// Returns a collection of Glyph objects representing the selection + /// borders and grab handles for a standard control. Note that + /// based on 'selectionType' the Glyphs returned will either: represent + /// a fully resizeable selection border with grab handles, a locked + /// selection border, or a single 'hidden' selection Glyph. /// public virtual GlyphCollection GetGlyphs(GlyphSelectionType selectionType) { - throw new NotImplementedException(SR.NotImplementedByDesign); + GlyphCollection glyphs = new GlyphCollection(); + if (selectionType != GlyphSelectionType.NotSelected) + { + Rectangle translatedBounds = BehaviorService.ControlRectInAdornerWindow(Control); + bool primarySelection = (selectionType == GlyphSelectionType.SelectedPrimary); + SelectionRules rules = SelectionRules; + if ((Locked) || (InheritanceAttribute == InheritanceAttribute.InheritedReadOnly)) + { + // the lock glyph + glyphs.Add(new LockedHandleGlyph(translatedBounds, primarySelection)); + //the four locked border glyphs + glyphs.Add(new LockedBorderGlyph(translatedBounds, SelectionBorderGlyphType.Top)); + glyphs.Add(new LockedBorderGlyph(translatedBounds, SelectionBorderGlyphType.Bottom)); + glyphs.Add(new LockedBorderGlyph(translatedBounds, SelectionBorderGlyphType.Left)); + glyphs.Add(new LockedBorderGlyph(translatedBounds, SelectionBorderGlyphType.Right)); + } + else if ((rules & SelectionRules.AllSizeable) == SelectionRules.None) + { + //the non-resizeable grab handle + glyphs.Add(new NoResizeHandleGlyph(translatedBounds, rules, primarySelection, MoveBehavior)); + //the four resizeable border glyphs + glyphs.Add(new NoResizeSelectionBorderGlyph(translatedBounds, rules, SelectionBorderGlyphType.Top, MoveBehavior)); + glyphs.Add(new NoResizeSelectionBorderGlyph(translatedBounds, rules, SelectionBorderGlyphType.Bottom, MoveBehavior)); + glyphs.Add(new NoResizeSelectionBorderGlyph(translatedBounds, rules, SelectionBorderGlyphType.Left, MoveBehavior)); + glyphs.Add(new NoResizeSelectionBorderGlyph(translatedBounds, rules, SelectionBorderGlyphType.Right, MoveBehavior)); + // enable the designeractionpanel for this control if it needs one + if (TypeDescriptor.GetAttributes(Component).Contains(DesignTimeVisibleAttribute.Yes) && behaviorService.DesignerActionUI != null) + { + Glyph dapGlyph = behaviorService.DesignerActionUI.GetDesignerActionGlyph(Component); + if (dapGlyph != null) + { + glyphs.Insert(0, dapGlyph); //we WANT to be in front of the other UI + } + } + } + else + { + //grab handles + if ((rules & SelectionRules.TopSizeable) != 0) + { + glyphs.Add(new GrabHandleGlyph(translatedBounds, GrabHandleGlyphType.MiddleTop, StandardBehavior, primarySelection)); + if ((rules & SelectionRules.LeftSizeable) != 0) + { + glyphs.Add(new GrabHandleGlyph(translatedBounds, GrabHandleGlyphType.UpperLeft, StandardBehavior, primarySelection)); + } + if ((rules & SelectionRules.RightSizeable) != 0) + { + glyphs.Add(new GrabHandleGlyph(translatedBounds, GrabHandleGlyphType.UpperRight, StandardBehavior, primarySelection)); + } + } + + if ((rules & SelectionRules.BottomSizeable) != 0) + { + glyphs.Add(new GrabHandleGlyph(translatedBounds, GrabHandleGlyphType.MiddleBottom, StandardBehavior, primarySelection)); + if ((rules & SelectionRules.LeftSizeable) != 0) + { + glyphs.Add(new GrabHandleGlyph(translatedBounds, GrabHandleGlyphType.LowerLeft, StandardBehavior, primarySelection)); + } + if ((rules & SelectionRules.RightSizeable) != 0) + { + glyphs.Add(new GrabHandleGlyph(translatedBounds, GrabHandleGlyphType.LowerRight, StandardBehavior, primarySelection)); + } + } + + if ((rules & SelectionRules.LeftSizeable) != 0) + { + glyphs.Add(new GrabHandleGlyph(translatedBounds, GrabHandleGlyphType.MiddleLeft, StandardBehavior, primarySelection)); + } + + if ((rules & SelectionRules.RightSizeable) != 0) + { + glyphs.Add(new GrabHandleGlyph(translatedBounds, GrabHandleGlyphType.MiddleRight, StandardBehavior, primarySelection)); + } + + //the four resizeable border glyphs + glyphs.Add(new SelectionBorderGlyph(translatedBounds, rules, SelectionBorderGlyphType.Top, StandardBehavior)); + glyphs.Add(new SelectionBorderGlyph(translatedBounds, rules, SelectionBorderGlyphType.Bottom, StandardBehavior)); + glyphs.Add(new SelectionBorderGlyph(translatedBounds, rules, SelectionBorderGlyphType.Left, StandardBehavior)); + glyphs.Add(new SelectionBorderGlyph(translatedBounds, rules, SelectionBorderGlyphType.Right, StandardBehavior)); + // enable the designeractionpanel for this control if it needs one + if (TypeDescriptor.GetAttributes(Component).Contains(DesignTimeVisibleAttribute.Yes) && behaviorService.DesignerActionUI != null) + { + Glyph dapGlyph = behaviorService.DesignerActionUI.GetDesignerActionGlyph(Component); + if (dapGlyph != null) + { + glyphs.Insert(0, dapGlyph); //we WANT to be in front of the other UI + } + } + } + } + return glyphs; } /// - /// Allows your component to support a design time user interface. A TabStrip - /// control, for example, has a design time user interface that allows the user - /// to click the tabs to change tabs. To implement this, TabStrip returns - /// true whenever the given point is within its tabs. + /// Allows your component to support a design time user interface. A TabStrip + /// control, for example, has a design time user interface that allows the user + /// to click the tabs to change tabs. To implement this, TabStrip returns + /// true whenever the given point is within its tabs. /// protected virtual bool GetHitTest(Point point) { @@ -228,57 +915,234 @@ protected virtual bool GetHitTest(Point point) } /// - /// Hooks the children of the given control. We need to do this for - /// child controls that are not in design mode, which is the case - /// for composite controls. + /// Hooks the children of the given control. We need to do this for + /// child controls that are not in design mode, which is the case + /// for composite controls. /// protected void HookChildControls(Control firstChild) { - throw new NotImplementedException(SR.NotImplementedByDesign); + foreach (Control child in firstChild.Controls) + { + if (child != null && host != null) + { + if (!(host.GetDesigner(child) is ControlDesigner)) + { + // No, no designer means we must replace the window target in this control. + IWindowTarget oldTarget = child.WindowTarget; + if (!(oldTarget is ChildWindowTarget)) + { + child.WindowTarget = new ChildWindowTarget(this, child, oldTarget); + child.ControlAdded += new ControlEventHandler(OnControlAdded); + } + if (child.IsHandleCreated) + { + Application.OleRequired(); + NativeMethods.RevokeDragDrop(child.Handle); + HookChildHandles(child.Handle); + } + else + { + child.HandleCreated += new EventHandler(OnChildHandleCreated); + } + // We only hook the children's children if there was no designer. We leave it up to the designer to hook its own children. + HookChildControls(child); + } + } + } } /// - /// Called by the host when we're first initialized. + /// Called by the host when we're first initialized. /// public override void Initialize(IComponent component) { - throw new NotImplementedException(SR.NotImplementedByDesign); + // Visibility works as follows If the control's property is not actually set, then set our shadow to true. Otherwise, grab the shadow value from the control directly and then set the control to be visible if it is not the root component. Root components will be set to visible = true in their own time by the view. + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(component.GetType()); + PropertyDescriptor visibleProp = props["Visible"]; + if (visibleProp == null || visibleProp.PropertyType != typeof(bool) || !visibleProp.ShouldSerializeValue(component)) + { + Visible = true; + } + else + { + Visible = (bool)visibleProp.GetValue(component); + } + + PropertyDescriptor enabledProp = props["Enabled"]; + if (enabledProp == null || enabledProp.PropertyType != typeof(bool) || !enabledProp.ShouldSerializeValue(component)) + { + Enabled = true; + } + else + { + Enabled = (bool)enabledProp.GetValue(component); + } + + initializing = true; + base.Initialize(component); + initializing = false; + // And get other commonly used services. + host = (IDesignerHost)GetService(typeof(IDesignerHost)); + + // this is to create the action in the DAP for this component if it requires docking/undocking logic + AttributeCollection attributes = TypeDescriptor.GetAttributes(Component); + DockingAttribute dockingAttribute = (DockingAttribute)attributes[typeof(DockingAttribute)]; + if (dockingAttribute != null && dockingAttribute.DockingBehavior != DockingBehavior.Never) + { + // create the action for this control + dockingAction = new DockingActionList(this); + //add our 'dock in parent' or 'undock in parent' action + if (GetService(typeof(DesignerActionService)) is DesignerActionService das) + { + das.Add(Component, dockingAction); + } + } + // Hook up the property change notifications we need to track. One for data binding. More for control add / remove notifications + dataBindingsCollectionChanged = new CollectionChangeEventHandler(DataBindingsCollectionChanged); + Control.DataBindings.CollectionChanged += dataBindingsCollectionChanged; + Control.ControlAdded += new ControlEventHandler(OnControlAdded); + Control.ControlRemoved += new ControlEventHandler(OnControlRemoved); + Control.ParentChanged += new EventHandler(OnParentChanged); + Control.SizeChanged += new EventHandler(OnSizeChanged); + Control.LocationChanged += new EventHandler(OnLocationChanged); + // Replace the control's window target with our own. This allows us to hook messages. + this.DesignerTarget = new DesignerWindowTarget(this); + // If the handle has already been created for this control, invoke OnCreateHandle so we can hookup our child control subclass. + if (Control.IsHandleCreated) + { + OnCreateHandle(); + } + + // If we are an inherited control, notify our inheritance UI + if (Inherited && host != null && host.RootComponent != component) + { + inheritanceUI = (InheritanceUI)GetService(typeof(InheritanceUI)); + if (inheritanceUI != null) + { + inheritanceUI.AddInheritedControl(Control, InheritanceAttribute.InheritanceLevel); + } + } + + // When we drag one control from one form to another, we will end up here. In this case we do not want to set the control to visible, so check ForceVisible. + if ((host == null || host.RootComponent != component) && ForceVisible) + { + Control.Visible = true; + } + // Always make controls enabled, event inherited ones. Otherwise we won't be able to select them. + Control.Enabled = true; + //we move enabledchanged below the set to avoid any possible stack overflows. this can occur if the parent is not enabled when we set enabled to true. + Control.EnabledChanged += new EventHandler(OnEnabledChanged); + // And force some shadow properties that we change in the course of initializing the form. + AllowDrop = Control.AllowDrop; + // update the Status Command + statusCommandUI = new StatusCommandUI(component.Site); } /// - /// ControlDesigner overrides this method to handle after-drop cases. + /// ControlDesigner overrides this method to handle after-drop cases. /// public override void InitializeExistingComponent(IDictionary defaultValues) { - throw new NotImplementedException(SR.NotImplementedByDesign); + base.InitializeExistingComponent(defaultValues); + // unhook any sited children that got ChildWindowTargets + foreach (Control c in Control.Controls) + { + if (c != null) + { + ISite site = c.Site; + ChildWindowTarget target = c.WindowTarget as ChildWindowTarget; + if (site != null && target != null) + { + c.WindowTarget = target.OldWindowTarget; + } + } + } } /// - /// ControlDesigner overrides this method. It will look at the default property for the control and, - /// if it is of type string, it will set this property's value to the name of the component. It only - /// does this if the designer has been configured with this option in the options service. This method - /// also connects the control to its parent and positions it. If you override this method, you should - /// always call base. + /// ControlDesigner overrides this method. It will look at the default property for the control and, + /// if it is of type string, it will set this property's value to the name of the component. It only + /// does this if the designer has been configured with this option in the options service. This method + /// also connects the control to its parent and positions it. If you override this method, you should + /// always call base. /// public override void InitializeNewComponent(IDictionary defaultValues) { - throw new NotImplementedException(SR.NotImplementedByDesign); + ISite site = Component.Site; + if (site != null) + { + PropertyDescriptor textProp = TypeDescriptor.GetProperties(Component)["Text"]; + if (textProp != null && textProp.PropertyType == typeof(string) && !textProp.IsReadOnly && textProp.IsBrowsable) + { + textProp.SetValue(Component, site.Name); + } + } + + if (defaultValues != null) + { + if (defaultValues["Parent"] is IComponent parent && GetService(typeof(IDesignerHost)) is IDesignerHost host) + { + if (host.GetDesigner(parent) is ParentControlDesigner parentDesigner) + { + parentDesigner.AddControl(Control, defaultValues); + } + + if (parent is Control parentControl) + { + // Some containers are docked differently (instead of DockStyle.None) when they are added through the designer + AttributeCollection attributes = TypeDescriptor.GetAttributes(Component); + DockingAttribute dockingAttribute = (DockingAttribute)attributes[typeof(DockingAttribute)]; + if (dockingAttribute != null && dockingAttribute.DockingBehavior != DockingBehavior.Never) + { + if (dockingAttribute.DockingBehavior == DockingBehavior.AutoDock) + { + bool onlyNonDockedChild = true; + foreach (Control c in parentControl.Controls) + { + if (c != Control && c.Dock == DockStyle.None) + { + onlyNonDockedChild = false; + break; + } + } + + if (onlyNonDockedChild) + { + PropertyDescriptor dockProp = TypeDescriptor.GetProperties(Component)["Dock"]; + if (dockProp != null && dockProp.IsBrowsable) + { + dockProp.SetValue(Component, DockStyle.Fill); + } + } + } + } + } + } + } + base.InitializeNewComponent(defaultValues); } /// - /// Called when the designer is intialized. This allows the designer to provide some - /// meaningful default values in the component. The default implementation of this - /// sets the components's default property to it's name, if that property is a string. + /// Called when the designer is intialized. This allows the designer to provide some + /// meaningful default values in the component. The default implementation of this + /// sets the components's default property to it's name, if that property is a string. /// - [Obsolete( - "This method has been deprecated. Use InitializeNewComponent instead. http://go.microsoft.com/fwlink/?linkid=14202")] + [Obsolete("This method has been deprecated. Use InitializeNewComponent instead. http://go.microsoft.com/fwlink/?linkid=14202")] public override void OnSetComponentDefaults() { - throw new NotImplementedException(SR.NotImplementedByDesign); + ISite site = Component.Site; + if (site != null) + { + PropertyDescriptor textProp = TypeDescriptor.GetProperties(Component)["Text"]; + if (textProp != null && textProp.IsBrowsable) + { + textProp.SetValue(Component, site.Name); + } + } } /// - /// Called when the context menu should be displayed + /// Called when the context menu should be displayed /// protected virtual void OnContextMenu(int x, int y) { @@ -286,23 +1150,32 @@ protected virtual void OnContextMenu(int x, int y) } /// - /// This is called immediately after the control handle has been created. + /// This is called immediately after the control handle has been created. /// protected virtual void OnCreateHandle() { - throw new NotImplementedException(SR.NotImplementedByDesign); + OnHandleChange(); + if (revokeDragDrop) + { + NativeMethods.RevokeDragDrop(Control.Handle); + } } /// - /// Called when a drag-drop operation enters the control designer view + /// Called when a drag-drop operation enters the control designer view /// protected virtual void OnDragEnter(DragEventArgs de) { - throw new NotImplementedException(SR.NotImplementedByDesign); + // unhook our events - we don't want to create an infinite loop. + Control control = Control; + DragEventHandler handler = new DragEventHandler(this.OnDragEnter); + control.DragEnter -= handler; + ((IDropTarget)Control).OnDragEnter(de); + control.DragEnter += handler; } /// - /// Called to cleanup a D&D operation + /// Called to cleanup a D&D operation /// protected virtual void OnDragComplete(DragEventArgs de) { @@ -310,276 +1183,1455 @@ protected virtual void OnDragComplete(DragEventArgs de) } /// - /// Called when a drag drop object is dropped onto the control designer view + /// Called when a drag drop object is dropped onto the control designer view /// protected virtual void OnDragDrop(DragEventArgs de) { - throw new NotImplementedException(SR.NotImplementedByDesign); + // unhook our events - we don't want to create an infinite loop. + Control control = Control; + DragEventHandler handler = new DragEventHandler(this.OnDragDrop); + control.DragDrop -= handler; + ((IDropTarget)Control).OnDragDrop(de); + control.DragDrop += handler; + OnDragComplete(de); } /// - /// Called when a drag-drop operation leaves the control designer view + /// Called when a drag-drop operation leaves the control designer view /// protected virtual void OnDragLeave(EventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + // unhook our events - we don't want to create an infinite loop. + Control control = Control; + EventHandler handler = new EventHandler(this.OnDragLeave); + control.DragLeave -= handler; + ((IDropTarget)Control).OnDragLeave(e); + control.DragLeave += handler; } /// - /// Called when a drag drop object is dragged over the control designer view + /// Called when a drag drop object is dragged over the control designer view /// protected virtual void OnDragOver(DragEventArgs de) { - throw new NotImplementedException(SR.NotImplementedByDesign); + // unhook our events - we don't want to create an infinite loop. + Control control = Control; + DragEventHandler handler = new DragEventHandler(this.OnDragOver); + control.DragOver -= handler; + ((IDropTarget)Control).OnDragOver(de); + control.DragOver += handler; } /// - /// Event handler for our GiveFeedback event, which is called when a drag operation - /// is in progress. The host will call us with - /// this when an OLE drag event happens. + /// Event handler for our GiveFeedback event, which is called when a drag operation + /// is in progress. The host will call us with + /// this when an OLE drag event happens. /// protected virtual void OnGiveFeedback(GiveFeedbackEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); } /// - /// Called in response to the left mouse button being pressed on a - /// component. It ensures that the component is selected. + /// Called in response to the left mouse button being pressed on a + /// component. It ensures that the component is selected. /// protected virtual void OnMouseDragBegin(int x, int y) { - throw new NotImplementedException(SR.NotImplementedByDesign); + // Ignore another mouse down if we are already in a drag + if (BehaviorService == null && mouseDragLast != InvalidPoint) + { + return; + } + + mouseDragLast = new Point(x, y); + ctrlSelect = (Control.ModifierKeys & Keys.Control) != 0; + ISelectionService sel = (ISelectionService)GetService(typeof(ISelectionService)); + // If the CTRL key isn't down, select this component, otherwise, we wait until the mouse up + // Make sure the component is selected + if (!ctrlSelect && sel != null) + { + sel.SetSelectedComponents(new object[] { Component }, SelectionTypes.Primary); + } + Control.Capture = true; } /// - /// Called at the end of a drag operation. This either commits or rolls back the - /// drag. + /// Called at the end of a drag operation. This either commits or rolls back the + /// drag. /// protected virtual void OnMouseDragEnd(bool cancel) { - throw new NotImplementedException(SR.NotImplementedByDesign); + mouseDragLast = InvalidPoint; + Control.Capture = false; + if (!mouseDragMoved) + { + // HACK HACK HACK + // ParentControlDesigner.Dispose depends on cancel having this behavior. + if (!cancel) + { + ISelectionService sel = (ISelectionService)GetService(typeof(ISelectionService)); + bool shiftSelect = (Control.ModifierKeys & Keys.Shift) != 0; + if (!shiftSelect && (ctrlSelect || (sel != null && !sel.GetComponentSelected(Component)))) + { + if (sel != null) + { + sel.SetSelectedComponents(new object[] { Component }, SelectionTypes.Primary); + } + ctrlSelect = false; + } + } + return; + } + mouseDragMoved = false; + ctrlSelect = false; + // And now finish the drag. + if (BehaviorService != null && BehaviorService.Dragging && cancel) + { + BehaviorService.CancelDrag = true; + } + // Leave this here in case we are doing a ComponentTray drag + if (selectionUISvc == null) + { + selectionUISvc = (ISelectionUIService)GetService(typeof(ISelectionUIService)); + } + + if (selectionUISvc == null) + { + return; + } + + // We must check to ensure that UI service is still in drag mode. It is possible that the user hit escape, which will cancel drag mode. + if (selectionUISvc.Dragging) + { + selectionUISvc.EndDrag(cancel); + } } /// - /// Called for each movement of the mouse. This will check to see if a drag operation - /// is in progress. If so, it will pass the updated drag dimensions on to the selection - /// UI service. + /// Called for each movement of the mouse. This will check to see if a drag operation + /// is in progress. If so, it will pass the updated drag dimensions on to the selection + /// UI service. /// protected virtual void OnMouseDragMove(int x, int y) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (!mouseDragMoved) + { + Size minDrag = SystemInformation.DragSize; + Size minDblClick = SystemInformation.DoubleClickSize; + minDrag.Width = Math.Max(minDrag.Width, minDblClick.Width); + minDrag.Height = Math.Max(minDrag.Height, minDblClick.Height); + // we have to make sure the mouse moved farther than the minimum drag distance before we actually start the drag + if (mouseDragLast == InvalidPoint || + (Math.Abs(mouseDragLast.X - x) < minDrag.Width && + Math.Abs(mouseDragLast.Y - y) < minDrag.Height)) + { + return; + } + else + { + mouseDragMoved = true; + // we're on the move, so we're not in a ctrlSelect + ctrlSelect = false; + } + } + + // Make sure the component is selected + // But only select it if it is not already the primary selection, and we want to toggle the current primary selection. + ISelectionService sel = (ISelectionService)GetService(typeof(ISelectionService)); + if (sel != null && !Component.Equals(sel.PrimarySelection)) + { + sel.SetSelectedComponents(new object[] { Component }, SelectionTypes.Primary | SelectionTypes.Toggle); + } + + if (BehaviorService != null && sel != null) + { + //create our list of controls-to-drag + ArrayList dragControls = new ArrayList(); + ICollection selComps = sel.GetSelectedComponents(); + //must identify a required parent to avoid dragging mixes of children + Control requiredParent = null; + foreach (IComponent comp in selComps) + { + if (comp is Control control) + { + if (requiredParent == null) + { + requiredParent = control.Parent; + } + else if (!requiredParent.Equals(control.Parent)) + { + continue;//mixed selection of different parents - don't add this + } + + if (host.GetDesigner(comp) is ControlDesigner des && (des.SelectionRules & SelectionRules.Moveable) != 0) + { + dragControls.Add(comp); + } + } + } + + //if we have controls-to-drag, create our new behavior and start the drag/drop operation + if (dragControls.Count > 0) + { + using (Graphics adornerGraphics = BehaviorService.AdornerWindowGraphics) + { + DropSourceBehavior dsb = new DropSourceBehavior(dragControls, Control.Parent, mouseDragLast); + BehaviorService.DoDragDrop(dsb); + } + } + } + + mouseDragLast = InvalidPoint; + mouseDragMoved = false; } /// - /// Called when the mouse first enters the control. This is forwarded to the parent - /// designer to enable the container selector. + /// Called when the mouse first enters the control. This is forwarded to the parent + /// designer to enable the container selector. /// protected virtual void OnMouseEnter() { - throw new NotImplementedException(SR.NotImplementedByDesign); + Control ctl = Control; + Control parent = ctl; + object parentDesigner = null; + while (parentDesigner == null && parent != null) + { + parent = parent.Parent; + if (parent != null) + { + object d = host.GetDesigner(parent); + if (d != this) + { + parentDesigner = d; + } + } + } + + if (parentDesigner is ControlDesigner cd) + { + cd.OnMouseEnter(); + } } /// - /// Called after the mouse hovers over the control. This is forwarded to the parent - /// designer to enabled the container selector. + /// Called after the mouse hovers over the control. This is forwarded to the parent + /// designer to enabled the container selector. /// protected virtual void OnMouseHover() { - throw new NotImplementedException(SR.NotImplementedByDesign); + Control ctl = Control; + Control parent = ctl; + object parentDesigner = null; + while (parentDesigner == null && parent != null) + { + parent = parent.Parent; + if (parent != null) + { + object d = host.GetDesigner(parent); + if (d != this) + { + parentDesigner = d; + } + } + } + + if (parentDesigner is ControlDesigner cd) + { + cd.OnMouseHover(); + } } /// - /// Called when the mouse first enters the control. This is forwarded to the parent - /// designer to enable the container selector. + /// Called when the mouse first enters the control. This is forwarded to the parent + /// designer to enable the container selector. /// protected virtual void OnMouseLeave() { - throw new NotImplementedException(SR.NotImplementedByDesign); + Control ctl = Control; + Control parent = ctl; + object parentDesigner = null; + while (parentDesigner == null && parent != null) + { + parent = parent.Parent; + if (parent != null) + { + object d = host.GetDesigner(parent); + if (d != this) + { + parentDesigner = d; + } + } + } + + if (parentDesigner is ControlDesigner cd) + { + cd.OnMouseLeave(); + } } /// - /// Called when the control we're designing has finished painting. This method - /// gives the designer a chance to paint any additional adornments on top of the - /// control. + /// Called when the control we're designing has finished painting. This method + /// gives the designer a chance to paint any additional adornments on top of the + /// control. /// protected virtual void OnPaintAdornments(PaintEventArgs pe) { - throw new NotImplementedException(SR.NotImplementedByDesign); + + // If this control is being inherited, paint it + if (inheritanceUI != null && pe.ClipRectangle.IntersectsWith(inheritanceUI.InheritanceGlyphRectangle)) + { + pe.Graphics.DrawImage(inheritanceUI.InheritanceGlyph, 0, 0); + } } /// - /// Called each time the cursor needs to be set. The ControlDesigner behavior here - /// will set the cursor to one of three things: - /// 1. If the toolbox service has a tool selected, it will allow the toolbox service to - /// set the cursor. - /// 2. If the selection UI service shows a locked selection, or if there is no location - /// property on the control, then the default arrow will be set. - /// 3. Otherwise, the four headed arrow will be set to indicate that the component can - /// be clicked and moved. - /// 4. If the user is currently dragging a component, the crosshair cursor will be used - /// instead of the four headed arrow. + /// Called each time the cursor needs to be set. The ControlDesigner behavior here + /// will set the cursor to one of three things: + /// 1. If the toolbox service has a tool selected, it will allow the toolbox service to + /// set the cursor. + /// 2. If the selection UI service shows a locked selection, or if there is no location + /// property on the control, then the default arrow will be set. + /// 3. Otherwise, the four headed arrow will be set to indicate that the component can + /// be clicked and moved. + /// 4. If the user is currently dragging a component, the crosshair cursor will be used + /// instead of the four headed arrow. /// protected virtual void OnSetCursor() { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (Control.Dock != DockStyle.None) + { + Cursor.Current = Cursors.Default; + } + else + { + if (toolboxSvc == null) + { + toolboxSvc = (IToolboxService)GetService(typeof(IToolboxService)); + } + + if (toolboxSvc != null && toolboxSvc.SetCursor()) + { + return; + } + + if (!locationChecked) + { + locationChecked = true; + try + { + hasLocation = TypeDescriptor.GetProperties(Component)["Location"] != null; + } + catch + { + } + } + + if (!hasLocation) + { + Cursor.Current = Cursors.Default; + return; + } + + if (Locked) + { + Cursor.Current = Cursors.Default; + return; + } + Cursor.Current = Cursors.SizeAll; + } } /// - /// Allows a designer to filter the set of properties - /// the component it is designing will expose through the - /// TypeDescriptor object. This method is called - /// immediately before its corresponding "Post" method. - /// If you are overriding this method you should call - /// the base implementation before you perform your own - /// filtering. + /// Allows a designer to filter the set of properties + /// the component it is designing will expose through the + /// TypeDescriptor object. This method is called + /// immediately before its corresponding "Post" method. + /// If you are overriding this method you should call + /// the base implementation before you perform your own + /// filtering. /// protected override void PreFilterProperties(IDictionary properties) { - throw new NotImplementedException(SR.NotImplementedByDesign); + base.PreFilterProperties(properties); + PropertyDescriptor prop; + // Handle shadowed properties + string[] shadowProps = new string[] { "Visible", "Enabled", "ContextMenu", "AllowDrop", "Location", "Name" }; + + Attribute[] empty = new Attribute[0]; + for (int i = 0; i < shadowProps.Length; i++) + { + prop = (PropertyDescriptor)properties[shadowProps[i]]; + if (prop != null) + { + properties[shadowProps[i]] = TypeDescriptor.CreateProperty(typeof(ControlDesigner), prop, empty); + } + } + + // replace this one seperately because it is of a different type (DesignerControlCollection) than the original property (ControlCollection) + PropertyDescriptor controlsProp = (PropertyDescriptor)properties["Controls"]; + + if (controlsProp != null) + { + Attribute[] attrs = new Attribute[controlsProp.Attributes.Count]; + controlsProp.Attributes.CopyTo(attrs, 0); + properties["Controls"] = TypeDescriptor.CreateProperty(typeof(ControlDesigner), "Controls", typeof(DesignerControlCollection), attrs); + } + + PropertyDescriptor sizeProp = (PropertyDescriptor)properties["Size"]; + if (sizeProp != null) + { + properties["Size"] = new CanResetSizePropertyDescriptor(sizeProp); + } + + // Now we add our own design time properties. + properties["Locked"] = TypeDescriptor .CreateProperty(typeof(ControlDesigner), "Locked", typeof(bool), new DefaultValueAttribute(false), BrowsableAttribute.Yes, CategoryAttribute.Design, DesignOnlyAttribute.Yes, new SRDescriptionAttribute(SR.lockedDescr)); } /// - /// Hooks the children of the given control. We need to do this for - /// child controls that are not in design mode, which is the case - /// for composite controls. + /// Hooks the children of the given control. We need to do this for + /// child controls that are not in design mode, which is the case + /// for composite controls. /// protected void UnhookChildControls(Control firstChild) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (host == null) + { + host = (IDesignerHost)GetService(typeof(IDesignerHost)); + } + + foreach (Control child in firstChild.Controls) + { + IWindowTarget oldTarget = null; + if (child != null) + { + // No, no designer means we must replace the window target in this control. + oldTarget = child.WindowTarget; + if (oldTarget is ChildWindowTarget target) + { + child.WindowTarget = target.OldWindowTarget; + } + } + if (!(oldTarget is DesignerWindowTarget)) + { + UnhookChildControls(child); + } + } } /// - /// This method should be called by the extending designer for each message - /// the control would normally receive. This allows the designer to pre-process - /// messages before allowing them to be routed to the control. + /// This method should be called by the extending designer for each message + /// the control would normally receive. This allows the designer to pre-process + /// messages before allowing them to be routed to the control. /// protected virtual void WndProc(ref Message m) { - throw new NotImplementedException(SR.NotImplementedByDesign); + IMouseHandler mouseHandler = null; + // We look at WM_NCHITTEST to determine if the mouse is in a live region of the control + if (m.Msg == NativeMethods.WM_NCHITTEST) + { + if (!inHitTest) + { + inHitTest = true; + Point pt = new Point((short)NativeMethods.Util.LOWORD(unchecked((int)(long)m.LParam)), (short)NativeMethods.Util.HIWORD(unchecked((int)(long)m.LParam))); + try + { + liveRegion = GetHitTest(pt); + } + catch (Exception e) + { + liveRegion = false; + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + } + inHitTest = false; + } + } + + // Check to see if the mouse is in a live region of the control and that the context key is not being fired + bool isContextKey = (m.Msg == NativeMethods.WM_CONTEXTMENU); + if (liveRegion && (IsMouseMessage(m.Msg) || isContextKey)) + { + // The ActiveX DataGrid control brings up a context menu on right mouse down when it is in edit mode. And, when we generate a WM_CONTEXTMENU message later, it calls DefWndProc() which by default calls the parent (formdesigner). The FormDesigner then brings up the AxHost context menu. This code causes recursive WM_CONTEXTMENU messages to be ignored till we return from the live region message. + if (m.Msg == NativeMethods.WM_CONTEXTMENU) + { + Debug.Assert(!inContextMenu, "Recursively hitting live region for context menu!!!"); + inContextMenu = true; + } + + try + { + DefWndProc(ref m); + } + finally + { + if (m.Msg == NativeMethods.WM_CONTEXTMENU) + { + inContextMenu = false; + } + if (m.Msg == NativeMethods.WM_LBUTTONUP) + { + // DTS SRX040824604234 TabControl loses shortcut menu options after adding ActiveX control. + OnMouseDragEnd(true); + } + } + return; + } + + // Get the x and y coordniates of the mouse message + int x = 0, y = 0; + + // Look for a mouse handler. + // CONSIDER - I really don't like this one bit. We need a centralized handler so we can do a global override for the tab order UI, but the designer is a natural fit for an object oriented UI. + if (m.Msg >= NativeMethods.WM_MOUSEFIRST && m.Msg <= NativeMethods.WM_MOUSELAST + || m.Msg >= NativeMethods.WM_NCMOUSEMOVE && m.Msg <= NativeMethods.WM_NCMBUTTONDBLCLK + || m.Msg == NativeMethods.WM_SETCURSOR) + { + if (eventSvc == null) + { + eventSvc = (IEventHandlerService)GetService(typeof(IEventHandlerService)); + } + if (eventSvc != null) + { + mouseHandler = (IMouseHandler)eventSvc.GetHandler(typeof(IMouseHandler)); + } + } + + if (m.Msg >= NativeMethods.WM_MOUSEFIRST && m.Msg <= NativeMethods.WM_MOUSELAST) + { + NativeMethods.POINT pt = new NativeMethods.POINT + { + x = NativeMethods.Util.SignedLOWORD(unchecked((int)(long)m.LParam)), + y = NativeMethods.Util.SignedHIWORD(unchecked((int)(long)m.LParam)) + }; + NativeMethods.MapWindowPoints(m.HWnd, IntPtr.Zero, pt, 1); + x = pt.x; + y = pt.y; + } + else if (m.Msg >= NativeMethods.WM_NCMOUSEMOVE && m.Msg <= NativeMethods.WM_NCMBUTTONDBLCLK) + { + x = NativeMethods.Util.SignedLOWORD(unchecked((int)(long)m.LParam)); + y = NativeMethods.Util.SignedHIWORD(unchecked((int)(long)m.LParam)); + } + + // This is implemented on the base designer for UI activation support. We call it so that we can support UI activation. + MouseButtons button = MouseButtons.None; + switch (m.Msg) + { + case NativeMethods.WM_CREATE: + DefWndProc(ref m); + // Only call OnCreateHandle if this is our OWN window handle -- the designer window procs are re-entered for child controls. + if (m.HWnd == Control.Handle) + { + OnCreateHandle(); + } + break; + + case NativeMethods.WM_GETOBJECT: + // See "How to Handle WM_GETOBJECT" in MSDN + if (NativeMethods.OBJID_CLIENT == unchecked((int)(long)m.LParam)) + { + // Get the IAccessible GUID + Guid IID_IAccessible = new Guid(NativeMethods.uuid_IAccessible); + // Get an Lresult for the accessibility Object for this control + IntPtr punkAcc; + try + { + IAccessible iacc = (IAccessible)AccessibilityObject; + if (iacc == null) + { + // Accessibility is not supported on this control + m.Result = (IntPtr)0; + } + else + { + // Obtain the Lresult + punkAcc = Marshal.GetIUnknownForObject(iacc); + try + { + m.Result = UnsafeNativeMethods.LresultFromObject(ref IID_IAccessible, m.WParam, punkAcc); + } + finally + { + Marshal.Release(punkAcc); + } + } + } + catch (Exception e) + { + throw e; + } + } + else + { // m.lparam != OBJID_CLIENT, so do default message processing + DefWndProc(ref m); + } + break; + + case NativeMethods.WM_MBUTTONDOWN: + case NativeMethods.WM_MBUTTONUP: + case NativeMethods.WM_MBUTTONDBLCLK: + case NativeMethods.WM_NCMOUSEHOVER: + case NativeMethods.WM_NCMOUSELEAVE: + case NativeMethods.WM_MOUSEWHEEL: + case NativeMethods.WM_NCMBUTTONDOWN: + case NativeMethods.WM_NCMBUTTONUP: + case NativeMethods.WM_NCMBUTTONDBLCLK: + // We intentionally eat these messages. + break; + case NativeMethods.WM_MOUSEHOVER: + if (mouseHandler != null) + { + mouseHandler.OnMouseHover(Component); + } + else + { + OnMouseHover(); + } + break; + case NativeMethods.WM_MOUSELEAVE: + OnMouseLeave(); + BaseWndProc(ref m); + break; + case NativeMethods.WM_NCLBUTTONDBLCLK: + case NativeMethods.WM_LBUTTONDBLCLK: + case NativeMethods.WM_NCRBUTTONDBLCLK: + case NativeMethods.WM_RBUTTONDBLCLK: + if ((m.Msg == NativeMethods.WM_NCRBUTTONDBLCLK || m.Msg == NativeMethods.WM_RBUTTONDBLCLK)) + { + button = MouseButtons.Right; + } + else + { + button = MouseButtons.Left; + } + + if (button == MouseButtons.Left) + { + // We handle doubleclick messages, and we also process our own simulated double clicks for controls that don't specify CS_WANTDBLCLKS. + if (mouseHandler != null) + { + mouseHandler.OnMouseDoubleClick(Component); + } + else + { + OnMouseDoubleClick(); + } + } + break; + case NativeMethods.WM_NCLBUTTONDOWN: + case NativeMethods.WM_LBUTTONDOWN: + case NativeMethods.WM_NCRBUTTONDOWN: + case NativeMethods.WM_RBUTTONDOWN: + if ((m.Msg == NativeMethods.WM_NCRBUTTONDOWN || m.Msg == NativeMethods.WM_RBUTTONDOWN)) + { + button = MouseButtons.Right; + } + else + { + button = MouseButtons.Left; + } + // We don't really want the focus, but we want to focus the designer. Below we handle WM_SETFOCUS and do the right thing. + NativeMethods.SendMessage(Control.Handle, NativeMethods.WM_SETFOCUS, 0, 0); + // We simulate doubleclick for things that don't... + if (button == MouseButtons.Left && IsDoubleClick(x, y)) + { + if (mouseHandler != null) + { + mouseHandler.OnMouseDoubleClick(Component); + } + else + { + OnMouseDoubleClick(); + } + } + else + { + toolPassThrough = false; + if (!EnableDragRect && button == MouseButtons.Left) + { + if (toolboxSvc == null) + { + toolboxSvc = (IToolboxService)GetService(typeof(IToolboxService)); + } + + if (toolboxSvc != null && toolboxSvc.GetSelectedToolboxItem((IDesignerHost)GetService(typeof(IDesignerHost))) != null) + { + // there is a tool to be dragged, so set passthrough and pass to the parent. + toolPassThrough = true; + } + } + else + { + toolPassThrough = false; + } + + if (toolPassThrough) + { + NativeMethods.SendMessage(Control.Parent.Handle, m.Msg, m.WParam, (IntPtr)GetParentPointFromLparam(m.LParam)); + return; + } + + if (mouseHandler != null) + { + mouseHandler.OnMouseDown(Component, button, x, y); + } + else if (button == MouseButtons.Left) + { + OnMouseDragBegin(x, y); + } + else if (button == MouseButtons.Right) + { + ISelectionService selSvc = (ISelectionService)GetService(typeof(ISelectionService)); + if (selSvc != null) + { + selSvc.SetSelectedComponents(new object[] { Component }, SelectionTypes.Primary); + } + } + lastMoveScreenX = x; + lastMoveScreenY = y; + } + break; + case NativeMethods.WM_NCMOUSEMOVE: + case NativeMethods.WM_MOUSEMOVE: + if ((unchecked((int)(long)m.WParam) & NativeMethods.MK_LBUTTON) != 0) + { + button = MouseButtons.Left; + } + else if ((unchecked((int)(long)m.WParam) & NativeMethods.MK_RBUTTON) != 0) + { + button = MouseButtons.Right; + toolPassThrough = false; + } + else + { + toolPassThrough = false; + } + + if (lastMoveScreenX != x || lastMoveScreenY != y) + { + if (toolPassThrough) + { + NativeMethods.SendMessage(Control.Parent.Handle, m.Msg, m.WParam, (IntPtr)GetParentPointFromLparam(m.LParam)); + return; + } + + if (mouseHandler != null) + { + mouseHandler.OnMouseMove(Component, x, y); + } + else if (button == MouseButtons.Left) + { + OnMouseDragMove(x, y); + } + } + lastMoveScreenX = x; + lastMoveScreenY = y; + // We eat WM_NCMOUSEMOVE messages, since we don't want the non-client area of design time controls to repaint on mouse move. + if (m.Msg == NativeMethods.WM_MOUSEMOVE) + { + BaseWndProc(ref m); + } + break; + case NativeMethods.WM_NCLBUTTONUP: + case NativeMethods.WM_LBUTTONUP: + case NativeMethods.WM_NCRBUTTONUP: + case NativeMethods.WM_RBUTTONUP: + // This is implemented on the base designer for UI activation support. + if ((m.Msg == NativeMethods.WM_NCRBUTTONUP || m.Msg == NativeMethods.WM_RBUTTONUP)) + { + button = MouseButtons.Right; + } + else + { + button = MouseButtons.Left; + } + + // And terminate the drag. + if (mouseHandler != null) + { + mouseHandler.OnMouseUp(Component, button); + } + else + { + if (toolPassThrough) + { + NativeMethods.SendMessage(Control.Parent.Handle, m.Msg, m.WParam, (IntPtr)GetParentPointFromLparam(m.LParam)); + toolPassThrough = false; + return; + } + + if (button == MouseButtons.Left) + { + OnMouseDragEnd(false); + } + } + + // clear any pass through. + toolPassThrough = false; + BaseWndProc(ref m); + break; + case NativeMethods.WM_PRINTCLIENT: + { + using (Graphics g = Graphics.FromHdc(m.WParam)) + { + using (PaintEventArgs e = new PaintEventArgs(g, Control.ClientRectangle)) + { + DefWndProc(ref m); + OnPaintAdornments(e); + } + } + } + break; + + case NativeMethods.WM_PAINT: + // First, save off the update region and call our base class. + if (OleDragDropHandler.FreezePainting) + { + NativeMethods.ValidateRect(m.HWnd, IntPtr.Zero); + break; + } + + if (Control == null) + { + break; + } + + NativeMethods.RECT clip = new NativeMethods.RECT(); + IntPtr hrgn = NativeMethods.CreateRectRgn(0, 0, 0, 0); + NativeMethods.GetUpdateRgn(m.HWnd, hrgn, false); + NativeMethods.GetUpdateRect(m.HWnd, ref clip, false); + Region r = Region.FromHrgn(hrgn); + Rectangle paintRect = Rectangle.Empty; + try + { + // Call the base class to do its own painting. + if (thrownException == null) + { + DefWndProc(ref m); + } + + // Now do our own painting. + Graphics gr = Graphics.FromHwnd(m.HWnd); + try + { + if (m.HWnd != Control.Handle) + { + // Re-map the clip rect we pass to the paint event args to our child coordinates. + NativeMethods.POINT pt = new NativeMethods.POINT(); + pt.x = 0; + pt.y = 0; + NativeMethods.MapWindowPoints(m.HWnd, Control.Handle, pt, 1); + gr.TranslateTransform(-pt.x, -pt.y); + NativeMethods.MapWindowPoints(m.HWnd, Control.Handle, ref clip, 2); + } + + paintRect = new Rectangle(clip.left, clip.top, clip.right - clip.left, clip.bottom - clip.top); + PaintEventArgs pevent = new PaintEventArgs(gr, paintRect); + try + { + gr.Clip = r; + if (thrownException == null) + { + OnPaintAdornments(pevent); + } + else + { + UnsafeNativeMethods.PAINTSTRUCT ps = new UnsafeNativeMethods.PAINTSTRUCT(); + UnsafeNativeMethods.BeginPaint(m.HWnd, ref ps); + PaintException(pevent, thrownException); + UnsafeNativeMethods.EndPaint(m.HWnd, ref ps); + } + } + finally + { + pevent.Dispose(); + } + } + finally + { + gr.Dispose(); + } + } + finally + { + r.Dispose(); + NativeMethods.DeleteObject(hrgn); + } + + if (OverlayService != null) + { + //this will allow any Glyphs to re-paint after this control and its designer has painted + paintRect.Location = Control.PointToScreen(paintRect.Location); + OverlayService.InvalidateOverlays(paintRect); + } + break; + case NativeMethods.WM_NCPAINT: + case NativeMethods.WM_NCACTIVATE: + if (m.Msg == NativeMethods.WM_NCACTIVATE) + { + DefWndProc(ref m); + } + else if (thrownException == null) + { + DefWndProc(ref m); + } + + // For some reason we dont always get an NCPAINT with the WM_NCACTIVATE usually this repros with themes on.... this can happen when someone calls RedrawWindow without the flags to send an NCPAINT. So that we dont double process this event, our calls to redraw window should not have RDW_ERASENOW | RDW_UPDATENOW. + + if (OverlayService != null) + { + if (Control != null && Control.Size != Control.ClientSize && Control.Parent != null) + { + // we have a non-client region to invalidate + Rectangle controlScreenBounds = new Rectangle(Control.Parent.PointToScreen(Control.Location), Control.Size); + Rectangle clientAreaScreenBounds = new Rectangle(Control.PointToScreen(Point.Empty), Control.ClientSize); + + using (Region nonClient = new Region(controlScreenBounds)) + { + nonClient.Exclude(clientAreaScreenBounds); + OverlayService.InvalidateOverlays(nonClient); + } + } + } + break; + + case NativeMethods.WM_SETCURSOR: + // We always handle setting the cursor ourselves. + if (liveRegion) + { + DefWndProc(ref m); + break; + } + + if (mouseHandler != null) + { + mouseHandler.OnSetCursor(Component); + } + else + { + OnSetCursor(); + } + break; + case NativeMethods.WM_SIZE: + if (this.thrownException != null) + { + Control.Invalidate(); + } + DefWndProc(ref m); + break; + case NativeMethods.WM_CANCELMODE: + // When we get cancelmode (i.e. you tabbed away to another window) then we want to cancel any pending drag operation! + OnMouseDragEnd(true); + DefWndProc(ref m); + break; + case NativeMethods.WM_SETFOCUS: + // We eat the focus unless the target is a ToolStrip edit node (TransparentToolStrip). If we eat the focus in that case, the Windows Narrator won't follow navigation via the keyboard. + // NB: "ToolStrip" is a bit of a misnomer here, because the ToolStripTemplateNode is also used for MenuStrip, StatusStrip, etc... + if (Control.FromHandle(m.HWnd) is ToolStripTemplateNode.TransparentToolStrip) + { + DefWndProc(ref m); + } + else if (host != null && host.RootComponent != null) + { + if (host.GetDesigner(host.RootComponent) is IRootDesigner rd) + { + ViewTechnology[] techs = rd.SupportedTechnologies; + if (techs.Length > 0) + { + if (rd.GetView(techs[0]) is Control view) + { + view.Focus(); + } + } + } + } + break; + case NativeMethods.WM_CONTEXTMENU: + if (inContextMenu) + { + break; + } + // We handle this in addition to a right mouse button. Why? Because we often eat the right mouse button, so it may never generate a WM_CONTEXTMENU. However, the system may generate one in response to an F-10. + x = NativeMethods.Util.SignedLOWORD(unchecked((int)(long)m.LParam)); + y = NativeMethods.Util.SignedHIWORD(unchecked((int)(long)m.LParam)); + + ToolStripKeyboardHandlingService keySvc = (ToolStripKeyboardHandlingService)GetService(typeof(ToolStripKeyboardHandlingService)); + bool handled = false; + if (keySvc != null) + { + handled = keySvc.OnContextMenu(x, y); + } + + if (!handled) + { + if (x == -1 && y == -1) + { + // for shift-F10 + Point p = Cursor.Position; + x = p.X; + y = p.Y; + } + OnContextMenu(x, y); + } + break; + default: + if (m.Msg == NativeMethods.WM_MOUSEENTER) + { + OnMouseEnter(); + BaseWndProc(ref m); + } + // We eat all key handling to the control. Controls generally should not be getting focus anyway, so this shouldn't happen. However, we want to prevent this as much as possible. + else if (m.Msg < NativeMethods.WM_KEYFIRST || m.Msg > NativeMethods.WM_KEYLAST) + { + DefWndProc(ref m); + } + break; + } } [ComVisible(true)] public class ControlDesignerAccessibleObject : AccessibleObject { + private readonly ControlDesigner _designer = null; + private Control _control = null; + private IDesignerHost _host = null; + private ISelectionService _selSvc = null; + public ControlDesignerAccessibleObject(ControlDesigner designer, Control control) { - throw new NotImplementedException(SR.NotImplementedByDesign); + _designer = designer; + _control = control; + } + + public override Rectangle Bounds + { + get => _control.AccessibilityObject.Bounds; } - public override Rectangle Bounds => throw new NotImplementedException(SR.NotImplementedByDesign); + public override string Description + { + get => _control.AccessibilityObject.Description; + } + + private IDesignerHost DesignerHost + { + get + { + if (_host == null) + { + _host = (IDesignerHost)_designer.GetService(typeof(IDesignerHost)); + } + return _host; + } + } - public override string Description => throw new NotImplementedException(SR.NotImplementedByDesign); + public override string DefaultAction + { + get => ""; + } - public override string DefaultAction => throw new NotImplementedException(SR.NotImplementedByDesign); + public override string Name + { + get => _control.Name; + } - public override string Name => throw new NotImplementedException(SR.NotImplementedByDesign); + public override AccessibleObject Parent + { + get => _control.AccessibilityObject.Parent; + } - public override AccessibleObject Parent => throw new NotImplementedException(SR.NotImplementedByDesign); + public override AccessibleRole Role + { + get => _control.AccessibilityObject.Role; + } - public override AccessibleRole Role => throw new NotImplementedException(SR.NotImplementedByDesign); + private ISelectionService SelectionService + { + get + { + if (_selSvc == null) + { + _selSvc = (ISelectionService)_designer.GetService(typeof(ISelectionService)); + } + return _selSvc; + } + } - public override AccessibleStates State => throw new NotImplementedException(SR.NotImplementedByDesign); + public override AccessibleStates State + { + get + { + AccessibleStates state = _control.AccessibilityObject.State; + ISelectionService s = SelectionService; + if (s != null) + { + if (s.GetComponentSelected(_control)) + { + state |= AccessibleStates.Selected; + } + if (s.PrimarySelection == _control) + { + state |= AccessibleStates.Focused; + } + } + return state; + } + } - public override string Value => throw new NotImplementedException(SR.NotImplementedByDesign); + public override string Value + { + get => _control.AccessibilityObject.Value; + } public override AccessibleObject GetChild(int index) { - throw new NotImplementedException(SR.NotImplementedByDesign); + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "ControlDesignerAccessibleObject.GetChild(" + index.ToString(CultureInfo.InvariantCulture) + ")"); + if (_control.AccessibilityObject.GetChild(index) is Control.ControlAccessibleObject childAccObj) + { + AccessibleObject cao = GetDesignerAccessibleObject(childAccObj); + if (cao != null) + { + return cao; + } + } + return _control.AccessibilityObject.GetChild(index); } - public override int GetChildCount() + public override int GetChildCount() => _control.AccessibilityObject.GetChildCount(); + + private AccessibleObject GetDesignerAccessibleObject(Control.ControlAccessibleObject cao) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (cao == null) + { + return null; + } + if (DesignerHost.GetDesigner(cao.Owner) is ControlDesigner ctlDesigner) + { + return ctlDesigner.AccessibilityObject; + } + return null; } public override AccessibleObject GetFocused() { - throw new NotImplementedException(SR.NotImplementedByDesign); + if ((State & AccessibleStates.Focused) != 0) + { + return this; + } + return base.GetFocused(); } public override AccessibleObject GetSelected() { - throw new NotImplementedException(SR.NotImplementedByDesign); + if ((State & AccessibleStates.Selected) != 0) + { + return this; + } + return base.GetFocused(); + } + + public override AccessibleObject HitTest(int x, int y) => _control.AccessibilityObject.HitTest(x, y); + } + + // Custom code dom serializer for the DesignerControlCollection. We need this so we can filter out controls that aren't sited in the host's container. + internal class DesignerControlCollectionCodeDomSerializer : CollectionCodeDomSerializer + { + protected override object SerializeCollection(IDesignerSerializationManager manager, CodeExpression targetExpression, Type targetType, ICollection originalCollection, ICollection valuesToSerialize) + { + ArrayList subset = new ArrayList(); + if (valuesToSerialize != null && valuesToSerialize.Count > 0) + { + foreach (object val in valuesToSerialize) + { + if (val is IComponent comp && comp.Site != null && !(comp.Site is INestedSite)) + { + subset.Add(comp); + } + } + } + return base.SerializeCollection(manager, targetExpression, targetType, originalCollection, subset); + } + } + + [ListBindable(false)] + [DesignerSerializer(typeof(DesignerControlCollectionCodeDomSerializer), typeof(CodeDomSerializer))] + internal class DesignerControlCollection : Control.ControlCollection, IList + { + Control.ControlCollection realCollection; + public DesignerControlCollection(Control owner) : base(owner) + { + realCollection = owner.Controls; + } + + public override int Count + { + get + { + return realCollection.Count; + } + } + + object ICollection.SyncRoot + { + get + { + return this; + } + } + + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + bool IList.IsFixedSize + { + get + { + return false; + } + } + + public new bool IsReadOnly + { + get + { + return realCollection.IsReadOnly; + } + } + + int IList.Add(object control) + { + return ((IList)realCollection).Add(control); + } + + public override void Add(Control c) + { + realCollection.Add(c); + } + + public override void AddRange(Control[] controls) + { + realCollection.AddRange(controls); + } + + bool IList.Contains(object control) + { + return ((IList)realCollection).Contains(control); + } + + public new void CopyTo(Array dest, int index) + { + realCollection.CopyTo(dest, index); + } + + public override bool Equals(object other) + { + return realCollection.Equals(other); + } + + public new IEnumerator GetEnumerator() + { + return realCollection.GetEnumerator(); + } + + public override int GetHashCode() + { + return realCollection.GetHashCode(); + } + + int IList.IndexOf(object control) + { + return ((IList)realCollection).IndexOf(control); + } + + void IList.Insert(int index, object value) + { + ((IList)realCollection).Insert(index, value); + } + + void IList.Remove(object control) + { + ((IList)realCollection).Remove(control); + } + + void IList.RemoveAt(int index) + { + ((IList)realCollection).RemoveAt(index); + } + + object IList.this[int index] + { + get + { + return ((IList)realCollection)[index]; + } + set + { + throw new NotSupportedException(); + } } - public override AccessibleObject HitTest(int x, int y) + public override int GetChildIndex(Control child, bool throwException) { - throw new NotImplementedException(SR.NotImplementedByDesign); + return realCollection.GetChildIndex(child, throwException); + } + + // we also need to redirect this guy + public override void SetChildIndex(Control child, int newIndex) + { + realCollection.SetChildIndex(child, newIndex); + } + + public override void Clear() + { + for (int i = realCollection.Count - 1; i >= 0; i--) + { + if (realCollection[i] != null && + realCollection[i].Site != null && + TypeDescriptor.GetAttributes(realCollection[i]).Contains(InheritanceAttribute.NotInherited)) + { + realCollection.RemoveAt(i); + } + } + } + } + + private class DockingActionList : DesignerActionList + { + private readonly ControlDesigner _designer; + private readonly IDesignerHost _host; + public DockingActionList(ControlDesigner owner) : base(owner.Component) + { + _designer = owner; + _host = GetService(typeof(IDesignerHost)) as IDesignerHost; + } + + private string GetActionName() + { + PropertyDescriptor dockProp = TypeDescriptor.GetProperties(Component)["Dock"]; + if (dockProp != null) + { + DockStyle dockStyle = (DockStyle)dockProp.GetValue(Component); + if (dockStyle == DockStyle.Fill) + { + return SR.DesignerShortcutUndockInParent; + } + else + { + return SR.DesignerShortcutDockInParent; + } + } + return null; + } + + public override DesignerActionItemCollection GetSortedActionItems() + { + DesignerActionItemCollection items = new DesignerActionItemCollection(); + string actionName = GetActionName(); + if (actionName != null) + { + items.Add(new DesignerActionVerbItem(new DesignerVerb(GetActionName(), OnDockActionClick))); + } + return items; + } + + private void OnDockActionClick(object sender, EventArgs e) + { + if (sender is DesignerVerb designerVerb && _host != null) + { + using (DesignerTransaction t = _host.CreateTransaction(designerVerb.Text)) + { + //set the dock prop to DockStyle.Fill + PropertyDescriptor dockProp = TypeDescriptor.GetProperties(Component)["Dock"]; + DockStyle dockStyle = (DockStyle)dockProp.GetValue(Component); + if (dockStyle == DockStyle.Fill) + { + dockProp.SetValue(Component, DockStyle.None); + } + else + { + dockProp.SetValue(Component, DockStyle.Fill); + } + t.Commit(); + } + } } } /// - /// This TransparentBehavior is associated with the BodyGlyph for - /// this ControlDesigner. When the BehaviorService hittests a glyph - /// w/a TransparentBehavior, all messages will be passed through - /// the BehaviorService directly to the ControlDesigner. - /// During a Drag operation, when the BehaviorService hittests + /// This TransparentBehavior is associated with the BodyGlyph for this ControlDesigner. When the BehaviorService hittests a glyph w/a TransparentBehavior, all messages will be passed through the BehaviorService directly to the ControlDesigner. During a Drag operation, when the BehaviorService hittests /// internal class TransparentBehavior : Behavior.Behavior { /// - /// Constructor that accepts the related ControlDesigner. + /// Constructor that accepts the related ControlDesigner. /// internal TransparentBehavior(ControlDesigner designer) { - throw new NotImplementedException(SR.NotImplementedByDesign); + this.designer = designer; } /// - /// This property performs a hit test on the ControlDesigner - /// to determine if the BodyGlyph should return '-1' for - /// hit testing (letting all messages pass directly to the - /// the control). + /// This property performs a hit test on the ControlDesigner to determine if the BodyGlyph should return '-1' for hit testing (letting all messages pass directly to the the control). /// - internal bool IsTransparent(Point p) - { - throw new NotImplementedException(SR.NotImplementedByDesign); - } + internal bool IsTransparent(Point p) => designer.GetHitTest(p); /// - /// Forwards DragDrop notification from the BehaviorService to - /// the related ControlDesigner. + /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. /// public override void OnDragDrop(Glyph g, DragEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + controlRect = Rectangle.Empty; + designer.OnDragDrop(e); } /// - /// Forwards DragDrop notification from the BehaviorService to - /// the related ControlDesigner. + /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. /// public override void OnDragEnter(Glyph g, DragEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (designer != null && designer.Control != null) + { + controlRect = designer.Control.RectangleToScreen(designer.Control.ClientRectangle); + } + designer.OnDragEnter(e); } /// - /// Forwards DragDrop notification from the BehaviorService to - /// the related ControlDesigner. + /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. /// public override void OnDragLeave(Glyph g, EventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + controlRect = Rectangle.Empty; + designer.OnDragLeave(e); } /// - /// Forwards DragDrop notification from the BehaviorService to - /// the related ControlDesigner. + /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. /// public override void OnDragOver(Glyph g, DragEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + // If we are not over a valid drop area, then do not allow the drag/drop. Now that all dragging/dropping is done via the behavior service and adorner window, we have to do our own validation, and cannot rely on the OS to do it for us. + if (e != null && controlRect != Rectangle.Empty && !controlRect.Contains(new Point(e.X, e.Y))) + { + e.Effect = DragDropEffects.None; + return; + } + designer.OnDragOver(e); } /// - /// Forwards DragDrop notification from the BehaviorService to - /// the related ControlDesigner. + /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. /// public override void OnGiveFeedback(Glyph g, GiveFeedbackEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + designer.OnGiveFeedback(e); } } } From 1699c1f02391139c20f2fe25c397caafdb34dc5a Mon Sep 17 00:00:00 2001 From: Judit Varsanyi Rozsa Date: Thu, 4 Apr 2019 17:09:27 -0700 Subject: [PATCH 02/10] port ControlDesigner --- .../src/BitmapSelector.cs | 0 src/Common/src/CompModSwitches.cs | 12 + src/Common/src/NativeMethods.cs | 1 + src/Common/src/SafeNativeMethods.cs | 220 +- src/Common/src/UnsafeNativeMethods.cs | 48 +- .../src/Resources/SR.resx | 311 +- .../src/Resources/xlf/SR.cs.xlf | 588 ++- .../src/Resources/xlf/SR.de.xlf | 588 ++- .../src/Resources/xlf/SR.es.xlf | 588 ++- .../src/Resources/xlf/SR.fr.xlf | 588 ++- .../src/Resources/xlf/SR.it.xlf | 588 ++- .../src/Resources/xlf/SR.ja.xlf | 588 ++- .../src/Resources/xlf/SR.ko.xlf | 588 ++- .../src/Resources/xlf/SR.pl.xlf | 588 ++- .../src/Resources/xlf/SR.pt-BR.xlf | 588 ++- .../src/Resources/xlf/SR.ru.xlf | 588 ++- .../src/Resources/xlf/SR.tr.xlf | 588 ++- .../src/Resources/xlf/SR.zh-Hans.xlf | 588 ++- .../src/Resources/xlf/SR.zh-Hant.xlf | 588 ++- .../src/System.Windows.Forms.Design.csproj | 6 +- .../Design/ComponentDesigner.cs | 24 +- .../Design/DesignerActionHeaderItem.cs | 17 + .../DesignerActionListsChangedEventArgs.cs | 52 + .../DesignerActionListsChangedEventHandler.cs | 12 + .../Design/DesignerActionListsChangedType.cs | 23 + .../Design/DesignerActionPanel.cs | 3037 ++++++++++++++ .../Design/DesignerActionTextItem.cs | 13 + .../Design/DesignerActionUIService.cs | 113 + .../DesignerActionUIStateChangeEventArgs.cs | 40 + ...DesignerActionUIStateChangeEventHandler.cs | 8 + .../Design/DesignerActionUIStateChangeType.cs | 13 + .../ComponentModel/Design/SelectionService.cs | 5 + .../ComponentModel/Design/VsPropertyGrid.cs | 69 + .../Windows/Forms/Design/AdornmentType.cs | 25 + .../Forms/Design/BaseContextMenuStrip.cs | 318 ++ .../Windows/Forms/Design/Behavior/Adorner.cs | 47 +- .../Forms/Design/Behavior/BehaviorService.cs | 28 +- .../Design/Behavior/DesignerActionBehavior.cs | 154 + .../Design/Behavior/DesignerActionGlyph.cs | 234 ++ .../Design/Behavior/DragAssistanceManager.cs | 1169 ++++++ .../Design/Behavior/DropSourceBehavior.cs | 1458 +++++++ .../Forms/Design/Behavior/GrabHandleGlyph.cs | 102 + .../Design/Behavior/GrabHandleGlyphType.cs | 21 + .../Design/Behavior/LockedBorderGlyph.cs | 45 + .../Design/Behavior/LockedHandleGlyph.cs | 39 + .../Design/Behavior/MiniLockedBorderGlyph.cs | 49 + .../Design/Behavior/NoResizeHandleGlyph.cs | 44 + .../Behavior/NoResizeSelectionBorderGlyph.cs | 73 + .../Design/Behavior/SelectionBorderGlyph.cs | 88 + .../Behavior/SelectionBorderGlyphType.cs | 18 + .../Design/Behavior/SelectionGlyphBase.cs | 70 + .../Windows/Forms/Design/Behavior/SnapLine.cs | 11 + .../Forms/Design/ChangeToolStripParentVerb.cs | 156 + .../Forms/Design/CollectionEditVerbManager.cs | 202 + .../System/Windows/Forms/Design/CommandSet.cs | 3540 +++++++++++++++++ .../Windows/Forms/Design/ComponentTray.cs | 2837 ++++++++++++- .../ContainerSelectorActiveEventArgs.cs | 31 + .../ContainerSelectorActiveEventArgsType.cs | 21 + .../ContainerSelectorActiveEventHandler.cs | 15 + .../Design/ContextMenuStripActionList.cs | 110 + .../Forms/Design/ContextMenuStripGroup.cs | 28 + .../Design/ContextMenuStripGroupCollection.cs | 50 + .../Windows/Forms/Design/ControlDesigner.cs | 1706 +++++--- .../Forms/Design/CustomMenuItemCollection.cs | 48 + .../Design/DesignerActionKeyboardBehavior.cs | 54 + .../Forms/Design/DesignerActionService.cs | 453 +++ .../Windows/Forms/Design/DesignerActionUI.cs | 1092 +++++ .../Forms/Design/DesignerActionVerbItem.cs | 45 + .../Forms/Design/DesignerActionVerbList.cs | 36 + .../Windows/Forms/Design/DesignerFrame.cs | 626 +++ .../Design/DesignerToolStripControlHost.cs | 79 + .../Windows/Forms/Design/DesignerUtils.cs | 963 +++++ .../Design/DesignerVerbToolStripMenuItem.cs | 43 + .../Windows/Forms/Design/DocumentDesigner.cs | 37 + .../Forms/Design/EditorServiceContext.cs | 203 + .../Forms/Design/FormDocumentDesigner.cs | 730 ++++ .../Forms/Design/GroupedContextMenuStrip.cs | 90 + .../Design/IContainsThemedScrollbarWindows.cs | 40 + .../Forms/Design/IMenuStatusHandler.cs | 24 + .../Windows/Forms/Design/IMouseHandler.cs | 40 + .../Windows/Forms/Design/IOverlayService.cs | 39 + .../Forms/Design/ISelectionUIService.cs | 101 + .../Forms/Design/ISplitWindowService.cs | 22 + .../Forms/Design/ISupportInSituService.cs | 30 + .../Windows/Forms/Design/InheritanceUI.cs | 119 + .../Forms/Design/ItemTypeToolStripMenuItem.cs | 72 + .../Forms/Design/NewItemsContextMenuStrip.cs | 76 + .../Forms/Design/ParentControlDesigner.cs | 325 +- .../Windows/Forms/Design/SelectionManager.cs | 478 +++ .../Windows/Forms/Design/SelectionStyles.cs | 27 + .../Forms/Design/SelectionUIService.cs | 1960 +++++++++ .../StandardCommandToolStripMenuItem.cs | 123 + .../Windows/Forms/Design/StandardGroups.cs | 22 + .../Forms/Design/StandardMenuStripVerb.cs | 525 +++ .../TemplateNodeCustomMenuItemCollection.cs | 167 + .../Design/TemplateNodeSelectionState.cs | 17 + .../Forms/Design/ToolStripActionList.cs | 196 + .../Design/ToolStripAdornerWindowService.cs | 310 ++ .../Windows/Forms/Design/ToolStripDesigner.cs | 2635 ++++++++++++ .../Forms/Design/ToolStripDesignerUtils.cs | 655 +++ .../Forms/Design/ToolStripDropDownDesigner.cs | 784 ++++ .../Design/ToolStripDropDownItemDesigner.cs | 37 + .../Forms/Design/ToolStripEditorManager.cs | 140 + .../Forms/Design/ToolStripInSituService.cs | 251 ++ .../Forms/Design/ToolStripItemBehavior.cs | 985 +++++ .../ToolStripItemCustomMenuItemCollection.cs | 701 ++++ .../Forms/Design/ToolStripItemDataObject.cs | 39 + .../Forms/Design/ToolStripItemDesigner.cs | 1360 +++++++ .../Forms/Design/ToolStripItemGlyph.cs | 69 + .../ToolStripKeyboardHandlingService.cs | 2132 ++++++++++ .../Forms/Design/ToolStripMenuItemDesigner.cs | 2793 +++++++++++++ .../Forms/Design/ToolStripTemplateNode.cs | 2074 ++++++++++ .../src/System.Windows.Forms.csproj | 1 + .../src/System/Windows/Forms/Message.cs | 1 - 114 files changed, 47241 insertions(+), 985 deletions(-) rename src/{System.Windows.Forms => Common}/src/BitmapSelector.cs (100%) create mode 100644 src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionHeaderItem.cs create mode 100644 src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionListsChangedEventArgs.cs create mode 100644 src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionListsChangedEventHandler.cs create mode 100644 src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionListsChangedType.cs create mode 100644 src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs create mode 100644 src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionTextItem.cs create mode 100644 src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIService.cs create mode 100644 src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventArgs.cs create mode 100644 src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventHandler.cs create mode 100644 src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeType.cs create mode 100644 src/System.Windows.Forms.Design/src/System/ComponentModel/Design/VsPropertyGrid.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/AdornmentType.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/BaseContextMenuStrip.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionBehavior.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionGlyph.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DragAssistanceManager.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DropSourceBehavior.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/GrabHandleGlyph.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/GrabHandleGlyphType.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/LockedBorderGlyph.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/LockedHandleGlyph.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/MiniLockedBorderGlyph.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeHandleGlyph.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeSelectionBorderGlyph.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionBorderGlyph.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionBorderGlyphType.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionGlyphBase.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ChangeToolStripParentVerb.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/CollectionEditVerbManager.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/CommandSet.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventArgs.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventArgsType.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventHandler.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContextMenuStripActionList.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContextMenuStripGroup.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContextMenuStripGroupCollection.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/CustomMenuItemCollection.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionKeyboardBehavior.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionService.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUI.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbItem.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbList.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerFrame.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerToolStripControlHost.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerUtils.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerVerbToolStripMenuItem.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/EditorServiceContext.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/GroupedContextMenuStrip.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IContainsThemedScrollbarWindows.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IMenuStatusHandler.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IMouseHandler.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IOverlayService.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ISelectionUIService.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ISplitWindowService.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ISupportInSituService.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/InheritanceUI.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ItemTypeToolStripMenuItem.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/NewItemsContextMenuStrip.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionManager.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionStyles.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionUIService.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardCommandToolStripMenuItem.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardGroups.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardMenuStripVerb.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/TemplateNodeCustomMenuItemCollection.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/TemplateNodeSelectionState.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripActionList.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripAdornerWindowService.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesigner.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesignerUtils.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDropDownDesigner.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDropDownItemDesigner.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripEditorManager.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripInSituService.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemBehavior.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemDataObject.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemDesigner.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemGlyph.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs create mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs diff --git a/src/System.Windows.Forms/src/BitmapSelector.cs b/src/Common/src/BitmapSelector.cs similarity index 100% rename from src/System.Windows.Forms/src/BitmapSelector.cs rename to src/Common/src/BitmapSelector.cs diff --git a/src/Common/src/CompModSwitches.cs b/src/Common/src/CompModSwitches.cs index ae6bdd9c862..e439e73bc21 100644 --- a/src/Common/src/CompModSwitches.cs +++ b/src/Common/src/CompModSwitches.cs @@ -323,5 +323,17 @@ public static BooleanSwitch TraceCollect { } } + private static BooleanSwitch commonDesignerServices; + public static BooleanSwitch CommonDesignerServices + { + get + { + if (commonDesignerServices == null) + { + commonDesignerServices = new BooleanSwitch("CommonDesignerServices", "Assert if any common designer service is not found."); + } + return commonDesignerServices; + } + } } } diff --git a/src/Common/src/NativeMethods.cs b/src/Common/src/NativeMethods.cs index 5418e1a9156..6043a30547c 100644 --- a/src/Common/src/NativeMethods.cs +++ b/src/Common/src/NativeMethods.cs @@ -1962,6 +1962,7 @@ public static bool Failed(int hr) { WM_MBUTTONDOWN = 0x0207, WM_MBUTTONUP = 0x0208, WM_MBUTTONDBLCLK = 0x0209, + WM_NCMOUSEHOVER = 0x02A0, WM_XBUTTONDOWN = 0x020B, WM_XBUTTONUP = 0x020C, WM_XBUTTONDBLCLK = 0x020D, diff --git a/src/Common/src/SafeNativeMethods.cs b/src/Common/src/SafeNativeMethods.cs index a1e3abda3eb..2b146f7e6c3 100644 --- a/src/Common/src/SafeNativeMethods.cs +++ b/src/Common/src/SafeNativeMethods.cs @@ -62,20 +62,20 @@ public static IntPtr CreateCompatibleBitmap(HandleRef hDC, int width, int height return System.Internal.HandleCollector.Add(IntCreateCompatibleBitmap(hDC, width, height), NativeMethods.CommonHandles.GDI); } - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool GetScrollInfo(HandleRef hWnd, int fnBar, [In, Out] NativeMethods.SCROLLINFO si); - [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool IsAccelerator(HandleRef hAccel, int cAccelEntries, [In] ref NativeMethods.MSG lpMsg, short[] lpwCmd); - [DllImport(ExternDll.Comdlg32, SetLastError=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Comdlg32, SetLastError=true, CharSet=CharSet.Auto)] public static extern bool ChooseFont([In, Out] NativeMethods.CHOOSEFONT cf); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int GetBitmapBits(HandleRef hbmp, int cbBuffer, byte[] lpvBits); - [DllImport(ExternDll.Comdlg32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Comdlg32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int CommDlgExtendedError(); [DllImport(ExternDll.Oleaut32, ExactSpelling=true, CharSet=CharSet.Unicode)] @@ -130,33 +130,33 @@ public static extern bool Rectangle( public static extern bool PatBlt(HandleRef hdc, int left, int top, int width, int height, int rop); - [DllImport(ExternDll.Kernel32, EntryPoint="GetThreadLocale", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Kernel32, EntryPoint="GetThreadLocale", CharSet=CharSet.Auto)] public static extern int GetThreadLCID(); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int GetMessagePos(); - [DllImport(ExternDll.User32, SetLastError=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, SetLastError=true, CharSet=CharSet.Auto)] public static extern int RegisterClipboardFormat(string format); - [DllImport(ExternDll.User32, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] public static extern int GetClipboardFormatName(int format, StringBuilder lpString, int cchMax); - [DllImport(ExternDll.Comdlg32, SetLastError=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Comdlg32, SetLastError=true, CharSet=CharSet.Auto)] public static extern bool ChooseColor([In, Out] NativeMethods.CHOOSECOLOR cc); - [DllImport(ExternDll.User32, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] public static extern int RegisterWindowMessage(string msg); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="DeleteObject", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="DeleteObject", CharSet=CharSet.Auto)] public static extern bool ExternalDeleteObject(HandleRef hObject); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="DeleteObject", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="DeleteObject", CharSet=CharSet.Auto)] internal static extern bool IntDeleteObject(HandleRef hObject); public static bool DeleteObject(HandleRef hObject) { @@ -168,7 +168,7 @@ public static bool DeleteObject(HandleRef hObject) { public static extern SafeNativeMethods.IFontDisp OleCreateIFontDispIndirect(NativeMethods.FONTDESC fd, ref Guid iid); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateSolidBrush", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateSolidBrush", CharSet=CharSet.Auto)] private static extern IntPtr IntCreateSolidBrush(int crColor); @@ -176,11 +176,11 @@ public static bool DeleteObject(HandleRef hObject) { public static IntPtr CreateSolidBrush(int crColor) { return System.Internal.HandleCollector.Add(IntCreateSolidBrush(crColor), NativeMethods.CommonHandles.GDI); } - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool SetWindowExtEx(HandleRef hDC, int x, int y, [In, Out] NativeMethods.SIZE size); - [DllImport(ExternDll.Kernel32, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto)] public static extern int FormatMessage(int dwFlags, HandleRef lpSource, int dwMessageId, int dwLanguageId, StringBuilder lpBuffer, int nSize, HandleRef arguments); @@ -308,7 +308,7 @@ public static IntPtr ImageList_Read(UnsafeNativeMethods.IStream pstm) { [DllImport(ExternDll.Comctl32)] public static extern int ImageList_WriteEx(HandleRef himl, int dwFlags, UnsafeNativeMethods.IStream pstm); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool TrackPopupMenuEx(HandleRef hmenu, int fuFlags, int x, int y, HandleRef hwnd, NativeMethods.TPMPARAMS tpm); [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] @@ -341,20 +341,20 @@ public static IntPtr ImageList_Read(UnsafeNativeMethods.IStream pstm) { [DllImport(ExternDll.User32, ExactSpelling = true)] public static extern bool EnumDisplayMonitors(HandleRef hdc, NativeMethods.COMRECT rcClip, NativeMethods.MonitorEnumProc lpfnEnum, IntPtr dwData); - [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, EntryPoint = "CreateHalftonePalette", CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, EntryPoint = "CreateHalftonePalette", CharSet = CharSet.Auto)] private static extern IntPtr /*HPALETTE*/ IntCreateHalftonePalette(HandleRef hdc); public static IntPtr /*HPALETTE*/ CreateHalftonePalette(HandleRef hdc) { return System.Internal.HandleCollector.Add(IntCreateHalftonePalette(hdc), NativeMethods.CommonHandles.GDI); } - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int GetPaletteEntries(HandleRef hpal, int iStartIndex, int nEntries, int[] lppe); [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] public static extern int GetTextMetricsW(HandleRef hDC, ref NativeMethods.TEXTMETRIC lptm); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateDIBSection", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateDIBSection", CharSet=CharSet.Auto)] private static extern IntPtr IntCreateDIBSection(HandleRef hdc, HandleRef pbmi, int iUsage, byte[] ppvBits, IntPtr hSection, int dwOffset); @@ -363,7 +363,7 @@ public static IntPtr CreateDIBSection(HandleRef hdc, HandleRef pbmi, int iUsage, return System.Internal.HandleCollector.Add(IntCreateDIBSection(hdc, pbmi, iUsage, ppvBits, hSection, dwOffset), NativeMethods.CommonHandles.GDI); } - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateBitmap", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateBitmap", CharSet=CharSet.Auto)] private static extern IntPtr /*HBITMAP*/ IntCreateBitmap(int nWidth, int nHeight, int nPlanes, int nBitsPerPixel, IntPtr lpvBits); @@ -372,7 +372,7 @@ public static IntPtr CreateDIBSection(HandleRef hdc, HandleRef pbmi, int iUsage, return System.Internal.HandleCollector.Add(IntCreateBitmap(nWidth, nHeight, nPlanes, nBitsPerPixel, lpvBits), NativeMethods.CommonHandles.GDI); } - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateBitmap", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateBitmap", CharSet=CharSet.Auto)] private static extern IntPtr /*HBITMAP*/ IntCreateBitmapShort(int nWidth, int nHeight, int nPlanes, int nBitsPerPixel, short[] lpvBits); @@ -381,7 +381,7 @@ public static IntPtr CreateDIBSection(HandleRef hdc, HandleRef pbmi, int iUsage, return System.Internal.HandleCollector.Add(IntCreateBitmapShort(nWidth, nHeight, nPlanes, nBitsPerPixel, lpvBits), NativeMethods.CommonHandles.GDI); } - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateBitmap", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateBitmap", CharSet=CharSet.Auto)] private static extern IntPtr /*HBITMAP*/ IntCreateBitmapByte(int nWidth, int nHeight, int nPlanes, int nBitsPerPixel, byte[] lpvBits); @@ -389,7 +389,7 @@ public static IntPtr CreateDIBSection(HandleRef hdc, HandleRef pbmi, int iUsage, public static IntPtr /*HBITMAP*/ CreateBitmap(int nWidth, int nHeight, int nPlanes, int nBitsPerPixel, byte[] lpvBits) { return System.Internal.HandleCollector.Add(IntCreateBitmapByte(nWidth, nHeight, nPlanes, nBitsPerPixel, lpvBits), NativeMethods.CommonHandles.GDI); } - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreatePatternBrush", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreatePatternBrush", CharSet=CharSet.Auto)] private static extern IntPtr /*HBRUSH*/ IntCreatePatternBrush(HandleRef hbmp); @@ -397,7 +397,7 @@ public static IntPtr CreateDIBSection(HandleRef hdc, HandleRef pbmi, int iUsage, public static IntPtr /*HBRUSH*/ CreatePatternBrush(HandleRef hbmp) { return System.Internal.HandleCollector.Add(IntCreatePatternBrush(hbmp), NativeMethods.CommonHandles.GDI); } - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateBrushIndirect", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateBrushIndirect", CharSet=CharSet.Auto)] private static extern IntPtr IntCreateBrushIndirect(NativeMethods.LOGBRUSH lb); @@ -405,7 +405,7 @@ public static IntPtr CreateDIBSection(HandleRef hdc, HandleRef pbmi, int iUsage, public static IntPtr CreateBrushIndirect(NativeMethods.LOGBRUSH lb) { return System.Internal.HandleCollector.Add(IntCreateBrushIndirect(lb), NativeMethods.CommonHandles.GDI); } - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreatePen", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreatePen", CharSet=CharSet.Auto)] private static extern IntPtr IntCreatePen(int nStyle, int nWidth, int crColor); @@ -415,26 +415,26 @@ public static IntPtr CreatePen(int nStyle, int nWidth, int crColor) { } - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool SetViewportExtEx(HandleRef hDC, int x, int y, NativeMethods.SIZE size); - [DllImport(ExternDll.User32, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] public static extern IntPtr LoadCursor(HandleRef hInst, int iconId); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public extern static bool GetClipCursor([In, Out] ref NativeMethods.RECT lpRect); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern IntPtr GetCursor(); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool GetIconInfo(HandleRef hIcon, [In, Out] NativeMethods.ICONINFO info); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int IntersectClipRect(HandleRef hDC, int x1, int y1, int x2, int y2); - [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="CopyImage", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="CopyImage", CharSet=CharSet.Auto)] private static extern IntPtr IntCopyImage(HandleRef hImage, int uType, int cxDesired, int cyDesired, int fuFlags); public static IntPtr CopyImage(HandleRef hImage, int uType, int cxDesired, int cyDesired, int fuFlags) { @@ -445,7 +445,7 @@ public static IntPtr CopyImageAsCursor(HandleRef hImage, int uType, int cxDesire } - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool AdjustWindowRectEx(ref NativeMethods.RECT lpRect, int dwStyle, bool bMenu, int dwExStyle); @@ -454,28 +454,33 @@ public static IntPtr CopyImageAsCursor(HandleRef hImage, int uType, int cxDesire public static extern bool AdjustWindowRectExForDpi(ref NativeMethods.RECT lpRect, int dwStyle, bool bMenu, int dwExStyle, uint dpi); - [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int DoDragDrop(IComDataObject dataObject, UnsafeNativeMethods.IOleDropSource dropSource, int allowedEffects, int[] finalEffect); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern IntPtr GetSysColorBrush(int nIndex); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool EnableWindow(HandleRef hWnd, bool enable); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool GetClientRect(HandleRef hWnd, [In, Out] ref NativeMethods.RECT rect); [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int GetDoubleClickTime(); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int GetUpdateRgn(HandleRef hwnd, HandleRef hrgn, bool fErase); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool ValidateRect(HandleRef hWnd, [In, Out] ref NativeMethods.RECT rect); + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ValidateRect(IntPtr hwnd, IntPtr prect); + // // WARNING: Don't uncomment this code unless you absolutelly need it. Use instead Marshal.GetLastWin32Error // and mark your PInvoke [DllImport(..., SetLastError=true)] @@ -486,36 +491,36 @@ public static IntPtr CopyImageAsCursor(HandleRef hImage, int uType, int cxDesire // definition for GetLastError and calling it. The common language runtime can make internal calls to APIs that // overwrite the operating system maintained GetLastError. // - //[DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + //[DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] //public extern static int GetLastError(); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int FillRect(HandleRef hdc, [In] ref NativeMethods.RECT rect, HandleRef hbrush); - [DllImport(ExternDll.Gdi32,ExactSpelling=true,CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32,ExactSpelling=true,CharSet=CharSet.Auto)] public static extern int /*COLORREF*/ GetTextColor(HandleRef hDC); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling = true, CharSet = CharSet.Auto)] public static extern int GetBkColor(HandleRef hDC); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int /*COLORREF*/ SetTextColor(HandleRef hDC, int crColor); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int SetBkColor(HandleRef hDC, int clr); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern IntPtr /* HPALETTE */SelectPalette(HandleRef hdc, HandleRef hpal, int bForceBackground); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool SetViewportOrgEx(HandleRef hDC, int x, int y, [In, Out] NativeMethods.POINT point); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateRectRgn", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateRectRgn", CharSet=CharSet.Auto)] private static extern IntPtr IntCreateRectRgn(int x1, int y1, int x2, int y2); @@ -523,60 +528,64 @@ public static IntPtr CopyImageAsCursor(HandleRef hImage, int uType, int cxDesire public static IntPtr CreateRectRgn(int x1, int y1, int x2, int y2) { return System.Internal.HandleCollector.Add(IntCreateRectRgn(x1, y1, x2, y2), NativeMethods.CommonHandles.GDI); } - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int CombineRgn(HandleRef hRgn, HandleRef hRgn1, HandleRef hRgn2, int nCombineMode); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int RealizePalette(HandleRef hDC); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool LPtoDP(HandleRef hDC, [In, Out] ref NativeMethods.RECT lpRect, int nCount); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool SetWindowOrgEx(HandleRef hDC, int x, int y, [In, Out] NativeMethods.POINT point); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool GetViewportOrgEx(HandleRef hDC, [In, Out] NativeMethods.POINT point); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int SetMapMode(HandleRef hDC, int nMapMode); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool IsWindowEnabled(HandleRef hWnd); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool IsWindowVisible(HandleRef hWnd); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool ReleaseCapture(); - [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int GetCurrentThreadId(); - [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto, SetLastError = true)] public static extern bool EnumWindows(EnumThreadWindowsCallback callback, IntPtr extraData); internal delegate bool EnumThreadWindowsCallback(IntPtr hWnd, IntPtr lParam); - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] public static extern int GetWindowThreadProcessId(HandleRef hWnd, out int lpdwProcessId); [return:MarshalAs(UnmanagedType.Bool)] - [DllImport(ExternDll.Kernel32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Kernel32, ExactSpelling = true, CharSet = CharSet.Auto)] public static extern bool GetExitCodeThread(HandleRef hWnd, out int lpdwExitCode); - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] public static extern bool ShowWindow(HandleRef hWnd, int nCmdShow); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool SetWindowPos(HandleRef hWnd, HandleRef hWndInsertAfter, int x, int y, int cx, int cy, int flags); - [DllImport(ExternDll.User32, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] + public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, + int x, int y, int cx, int cy, int flags); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] public static extern int GetWindowTextLength(HandleRef hWnd); // this is a wrapper that comctl exposes for the NT function since it doesn't exist natively on 95. @@ -587,114 +596,121 @@ public static bool TrackMouseEvent(NativeMethods.TRACKMOUSEEVENT tme) { // only on NT - not on 95 - comctl32 has a wrapper for 95 and NT. return _TrackMouseEvent(tme); } - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool RedrawWindow(HandleRef hwnd, ref NativeMethods.RECT rcUpdate, HandleRef hrgnUpdate, int flags); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool RedrawWindow(HandleRef hwnd, NativeMethods.COMRECT rcUpdate, HandleRef hrgnUpdate, int flags); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] + public static extern bool RedrawWindow(IntPtr hwnd, NativeMethods.COMRECT rcUpdate, IntPtr hrgnUpdate, int flags); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool InvalidateRect(HandleRef hWnd, ref NativeMethods.RECT rect, bool erase); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool InvalidateRect(HandleRef hWnd, NativeMethods.COMRECT rect, bool erase); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool InvalidateRgn(HandleRef hWnd, HandleRef hrgn, bool erase); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool UpdateWindow(HandleRef hWnd); - [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int GetCurrentProcessId(); - [DllImport(ExternDll.User32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int ScrollWindowEx(HandleRef hWnd, int nXAmount, int nYAmount, NativeMethods.COMRECT rectScrollRegion, ref NativeMethods.RECT rectClip, HandleRef hrgnUpdate, ref NativeMethods.RECT prcUpdate, int flags); - [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int GetThreadLocale(); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool MessageBeep(int type); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool DrawMenuBar(HandleRef hWnd); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public extern static bool IsChild(HandleRef parent, HandleRef child); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern IntPtr SetTimer(HandleRef hWnd, int nIDEvent, int uElapse, IntPtr lpTimerFunc); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool KillTimer(HandleRef hwnd, int idEvent); - [DllImport(ExternDll.User32, CharSet=System.Runtime.InteropServices.CharSet.Auto), + [DllImport(ExternDll.User32, CharSet=CharSet.Auto), SuppressMessage("Microsoft.Usage", "CA2205:UseManagedEquivalentsOfWin32Api")] public static extern int MessageBox(HandleRef hWnd, string text, string caption, int type); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern IntPtr SelectObject(HandleRef hDC, HandleRef hObject); - [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int GetTickCount(); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool ScrollWindow(HandleRef hWnd, int nXAmount, int nYAmount, ref NativeMethods.RECT rectScrollRegion, ref NativeMethods.RECT rectClip); - [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern IntPtr GetCurrentProcess(); - [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern IntPtr GetCurrentThread(); [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - [DllImport(ExternDll.Kernel32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Kernel32, ExactSpelling = true, CharSet = CharSet.Auto)] public extern static bool SetThreadLocale(int Locale); - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] public static extern bool IsWindowUnicode(HandleRef hWnd); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool DrawEdge(HandleRef hDC, ref NativeMethods.RECT rect, int edge, int flags); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool DrawFrameControl(HandleRef hDC, ref NativeMethods.RECT rect, int type, int state); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int GetClipRgn(HandleRef hDC, HandleRef hRgn); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int GetRgnBox(HandleRef hRegion, ref NativeMethods.RECT clipRect); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int SelectClipRgn(HandleRef hDC, HandleRef hRgn); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int SetROP2(HandleRef hDC, int nDrawMode); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool DrawIcon(HandleRef hDC, int x, int y, HandleRef hIcon); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool DrawIconEx(HandleRef hDC, int x, int y, HandleRef hIcon, int width, int height, int iStepIfAniCursor, HandleRef hBrushFlickerFree, int diFlags); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int SetBkMode(HandleRef hDC, int nBkMode); - [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool BitBlt(HandleRef hDC, int x, int y, int nWidth, int nHeight, HandleRef hSrcDC, int xSrc, int ySrc, int dwRop); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, ExactSpelling = true, CharSet = CharSet.Auto)] + public static extern bool BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool ShowCaret(HandleRef hWnd); - [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern bool HideCaret(HandleRef hWnd); [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] @@ -853,6 +869,12 @@ public static extern bool BitBlt(HandleRef hDC, int x, int y, int nWidth, int nH [DllImport(ExternDll.Kernel32, SetLastError = true)] public static extern IntPtr OpenProcess(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId); + [DllImport(ExternDll.Gdi32, ExactSpelling = true, CharSet = CharSet.Auto)] + public static extern bool RoundRect(HandleRef hDC, int left, int top, int right, int bottom, int width, int height); + + [DllImport(ExternDll.Uxtheme, CharSet = CharSet.Auto)] + public extern static int SetWindowTheme(IntPtr hWnd, string subAppName, string subIdList); + internal const int PROCESS_QUERY_INFORMATION = 0x0400; internal const int PROCESS_VM_READ = 0x0010; diff --git a/src/Common/src/UnsafeNativeMethods.cs b/src/Common/src/UnsafeNativeMethods.cs index 8cad0eec50d..8a487aa65ae 100644 --- a/src/Common/src/UnsafeNativeMethods.cs +++ b/src/Common/src/UnsafeNativeMethods.cs @@ -92,6 +92,9 @@ internal static class UnsafeNativeMethods { public static extern int ReadClassStg(HandleRef pStg, [In, Out] ref Guid pclsid); + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] + public static extern int GetMessageTime(); + [DllImport(ExternDll.Ole32, SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true)] internal extern static void CoTaskMemFree(IntPtr pv); @@ -998,7 +1001,11 @@ public static int SetWindowRgn(HandleRef hwnd, HandleRef hrgn, bool fRedraw) { public static extern IntPtr GetWindow(HandleRef hWnd, int uCmd); [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] - + + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetWindow(IntPtr hWnd, int uCmd); + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] + public static extern IntPtr GetDlgItem(HandleRef hWnd, int nIDDlgItem); [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto)] @@ -1013,17 +1020,7 @@ public static int SetWindowRgn(HandleRef hwnd, HandleRef hrgn, bool fRedraw) { public static extern IntPtr CallWindowProc(IntPtr wndProc, IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); - -/* - [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] - public static extern IntPtr GetProp(HandleRef hWnd, int atom); - [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] - public static extern IntPtr GetProp(HandleRef hWnd, string name); - [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] - public static extern IntPtr RemoveProp(HandleRef hWnd, short atom); - [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] - public static extern IntPtr RemoveProp(HandleRef hWnd, string propName); - */ + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern short GlobalDeleteAtom(short atom); @@ -1038,10 +1035,7 @@ public static extern IntPtr CallWindowProc(IntPtr wndProc, IntPtr hWnd, int msg, [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] public static extern bool GetClassInfo(HandleRef hInst, string lpszClass, IntPtr h); -/* - [DllImport(ExternDll.Shell32, CharSet=CharSet.Auto)] - public static extern int SHGetFolderPath(HandleRef hwndOwner, int nFolder, HandleRef hToken, int dwFlags, StringBuilder lpszPath); -*/ + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int GetSystemMetrics(int nIndex); @@ -1051,6 +1045,9 @@ public static extern IntPtr CallWindowProc(IntPtr wndProc, IntPtr hWnd, int msg, public static extern int GetSystemMetricsForDpi(int nIndex, uint dpi); + [DllImport(ExternDll.Gdi32, CharSet = CharSet.Auto)] + public static extern bool GetTextMetrics(HandleRef hdc, NativeMethods.TEXTMETRIC tm); + /// /// Tries to get system metrics for the dpi. dpi is ignored if "GetSystemMetricsForDpi" is not available on the OS that this application is running. /// @@ -1140,6 +1137,10 @@ public static bool TrySystemParametersInfoForDpi(int nAction, int nParam, [In, O [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern int RevokeDragDrop(HandleRef hwnd); + + [DllImport(ExternDll.Ole32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int RevokeDragDrop(IntPtr hwnd); [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] public static extern bool PeekMessage([In, Out] ref NativeMethods.MSG msg, HandleRef hwnd, int msgMin, int msgMax, int remove); @@ -1166,6 +1167,11 @@ public static bool TrySystemParametersInfoForDpi(int nAction, int nParam, [In, O [DllImport(ExternDll.Oleacc, ExactSpelling=true, CharSet=CharSet.Auto)] public static extern IntPtr LresultFromObject(ref Guid refiid, IntPtr wParam, HandleRef pAcc); + + [DllImport(ExternDll.Oleacc, ExactSpelling = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr LresultFromObject(ref Guid refiid, IntPtr wParam, IntPtr pAcc); + [DllImport(ExternDll.Oleacc, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] public static extern int CreateStdAccessibleObject(HandleRef hWnd, int objID, ref Guid refiid, [In, Out, MarshalAs(UnmanagedType.Interface)] ref object pAcc); @@ -1190,7 +1196,11 @@ public static bool TrySystemParametersInfoForDpi(int nAction, int nParam, [In, O public static IntPtr BeginPaint(HandleRef hWnd, [In, Out, MarshalAs(UnmanagedType.LPStruct)] ref NativeMethods.PAINTSTRUCT lpPaint) { return System.Internal.HandleCollector.Add(IntBeginPaint(hWnd, ref lpPaint), NativeMethods.CommonHandles.HDC); } - + + [DllImport(ExternDll.User32, ExactSpelling = true, EntryPoint = "BeginPaint", CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr BeginPaint(IntPtr hWnd, [In, Out] ref NativeMethods.PAINTSTRUCT lpPaint); + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="EndPaint", CharSet=CharSet.Auto)] private static extern bool IntEndPaint(HandleRef hWnd, ref NativeMethods.PAINTSTRUCT lpPaint); @@ -1199,6 +1209,10 @@ public static bool EndPaint(HandleRef hWnd, [In, MarshalAs(UnmanagedType.LPStruc return IntEndPaint(hWnd, ref lpPaint); } + [DllImport(ExternDll.User32, ExactSpelling = true, EntryPoint = "EndPaint", CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool EndPaint(IntPtr hWnd, ref NativeMethods.PAINTSTRUCT lpPaint); + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="GetDC", CharSet=CharSet.Auto)] private static extern IntPtr IntGetDC(HandleRef hWnd); diff --git a/src/System.Windows.Forms.Design/src/Resources/SR.resx b/src/System.Windows.Forms.Design/src/Resources/SR.resx index 3d60caa0f0b..ac5349a43b5 100644 --- a/src/System.Windows.Forms.Design/src/Resources/SR.resx +++ b/src/System.Windows.Forms.Design/src/Resources/SR.resx @@ -420,4 +420,313 @@ Undock in Parent Container - + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + + The Locked property determines if we can move or resize the control. + + + '{1}' is not a valid value for '{0}'. + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + + The field '{0}' could not be found on the target object. Make sure that the field is defined as an instance variable on the target object and has the correct scope. + + + Array rank '{0}' is too high. Visual Studio can only save and load arrays with a rank of 1. + + + Code statements for the object '{0}' were lost during serialization. This may have been a result of another object misbehaving during serialization. + + + The source code contains a reference to the class definition, but the class definition cannot be found. + + + The object '{0}' failed to serialize itself. It may not support code generation. + + + The type '{0}' has no event named '{1}'. + + + The type '{0}' has no field named '{1}'. + + + The type '{0}' has no property named '{1}'. + + + Could not find type '{0}'. Please make sure that the assembly that contains this type is referenced. If this type is a part of your development project, make sure that the project has been successfully built using settings for your current platform or Any CPU. + + + The variable '{0}' is either undeclared or was never assigned. + + + &About... + + + &Contents + + + &Copy + + + Create Standard Menu + + + &Customize + + + Cu&t + + + &Edit + + + E&xit + + + &File + + + &Help + + + &Index + + + &New + + + &Open + + + &Options + + + &Paste + + + &Print + + + Print Pre&view + + + &Redo + + + &Save + + + Save &As + + + &Search + + + Select &All + + + &Tools + + + &Undo + + + C&ut + + + Dock: + + + Changes the Dock property + + + GripStyle: + + + Changes the GripStyle property + + + Layout && Appearance + + + RenderMode: + + + Changes the RenderMode property + + + Adding {0} Item + + + AllowItemReorder and AllowDrop cannot both be true. + + + ToolStrip New Item create Transaction. + + + Embed in ToolStripContainer + + + Embeds the current ToolStrip in ToolStripContainer + + + &Insert Standard Items + + + Inserts standard items in the current ToolStrip + + + Type Here + + + Type Text for ToolStripMenuItem + + + Add Menu Item + + + Add ToolStripStatusLabel + + + Add ToolStripButton + + + New item selection + + + Removing Item + + + ToolStrip MenuItem Insert in DropDown Transaction. + + + &Edit Items... + + + &Insert + + + ToolStripItem Property Change Transaction. + + + ToolStripItem Morphing Transaction. + + + Cannot add ToolStripSeparator to MenuStrip. + + + Type '{0}' is not available in the target framework. + + + ShowCheckMargin + + + Toggles the ShowCheckMargin property + + + ShowImageMargin + + + Toggles the ShowImageMargin property + + + Duplicate declaration of member '{0}' + + + The name '{0}' is already used by another object. + + + Cannot name the object '{0}' because it is already named '{1}'. + + + Elements of type {0} are not supported. The serializer expects the element to be one of the following: {1}. + + + Error reading resources from the resource file for the culture {0}: {1} + + + Error reading resources from the resource file for the default culture: {0} + + + Adding Item + + + &Edit DropDownItems... + + + Con&vert To + + + The serialization store is closed. New objects cannot be added to a closed store. + + + Complete deserialization of {0} failed. + + + This type of serialization store is not supported. Use a store returned by the CreateStore method. + + + Align To &Grid + + + &Bring To Front + + + C&opy + + + &Cut + + + &Delete + + + &Document Outline + + + &Lock Controls + + + &Paste + + + &Properties + + + &Select + + + &Send To Back + + + View &Code + + + You cannot create a new session because this serialization manager already has an active serialization session. + + + Type '{0}' does not have a constructor with parameters of types {1}. + + + This method cannot be invoked because the serialization manager does not have an active serialization session. + + + This method cannot be invoked because the serialization manager has an active serialization session. + + + Members of type '{0}' cannot be serialized. + + + He&lp + + + Set I&mage... + + + '{0}' + + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf index 0aa595223a5..3fcf9af0a78 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation Generování optimalizovaného kódu @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. Tento objekt IDataObject nepodporuje funkci SetData. + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. Tuto metodu nebo objekt návrh neimplementuje. @@ -272,6 +507,11 @@ Winforms Designer není na této platformě podporován. + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf index aa40f830a0a..bc3854600ef 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation Optimierte Codegenerierung @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. Dieses IDataObject unterstützt SetData nicht. + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. Diese Methode/dieses Objekt wird entwurfsbedingt nicht implementiert. @@ -272,6 +507,11 @@ Der Windows Forms-Designer wird auf dieser Plattform nicht unterstützt. + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf index 85969cf90ad..4c1f204af03 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation Generación de código optimizado @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. IDataObject no es compatible con SetData. + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. Este objeto o método no se implementa por diseño. @@ -272,6 +507,11 @@ Winforms Designer no se admite en esta plataforma. + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf index 241c3c2c03f..d5f96d9780e 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation Génération de code optimisé @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. Ce IDataObject ne prend pas en charge SetData. + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. Cette méthode ou cet objet n'est pas implémenté(e) dans la conception. @@ -272,6 +507,11 @@ Le concepteur WinForms n'est pas pris en charge sur cette plateforme. + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf index 05fbe81c5d3..3aa6c0c967b 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation Generazione del codice ottimizzata @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. Questo oggetto IDataObject non supporta SetData. + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. Questo metodo/oggetto non è implementato per impostazione predefinita. @@ -272,6 +507,11 @@ Winforms Designer non è supportato in questa piattaforma. + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf index 4158a17e30c..d44cdba2d4e 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation 最適化されたコード生成 @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. この IDataObject は SetData をサポートしていません。 + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. このメソッド/オブジェクトはデザインによって実装されていません。 @@ -272,6 +507,11 @@ Windows フォーム デザイナーは、このプラットフォームではサポートされていません。 + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf index a2252a60dea..6738f3c2e40 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation 최적화된 코드 생성 @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. IDataObject는 SetData를 지원하지 않습니다. + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. 이 메서드/개체는 의도적으로 구현되지 않습니다. @@ -272,6 +507,11 @@ Winforms 디자이너는 이 플랫폼에서 지원되지 않습니다. + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf index 32a069798c2..eb4213ce9a8 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation Zoptymalizowane generowanie kodu @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. Ten element IDataObject nie obsługuje elementu SetData. + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. Ta metoda/obiekt nie jest domyślnie implementowana. @@ -272,6 +507,11 @@ Projektant formularzy systemu Windows nie jest obsługiwany na tej platformie. + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf index 722b8d90f1f..c55e3c61513 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation Geração de Código Otimizada @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. Este IDataObject não oferece suporte para SetData. + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. Este método/objeto não é implementado por design. @@ -272,6 +507,11 @@ O Designer de Formulários do Windows não é compatível com esta plataforma. + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf index 525c4bfb635..aed8ecc601f 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation Создание оптимизированного кода @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. Этот объект IDataObject не поддерживает SetData. + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. Этот метод или объект не реализован намеренно. @@ -272,6 +507,11 @@ Конструктор Windows Forms не поддерживается на этой платформе. + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf index 098803be787..13695f566cc 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation En İyi Duruma Getirilmiş Kod Oluşturma @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. IDataObject SetData'yı desteklemiyor. + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. Tasarım gereği, bu metot/nesne uygulanmadı. @@ -272,6 +507,11 @@ Winforms Tasarımcısı bu platformda desteklenmiyor. + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf index 2be88d9ccbe..334fda645f1 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation 优化代码生成 @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. 此 IDataObject 不支持 SetData。 + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. 未按设计实现此方法/对象。 @@ -272,6 +507,11 @@ 此平台上不支持 Winforms Designer。 + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf index 3ce02aabe46..fea468d0b2a 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf @@ -2,6 +2,36 @@ + + Copy and move {0} + Copy and move {0} + + + + Copy and move {0} Controls + Copy and move {0} Controls + + + + Move {0} + Move {0} + + + + Move {0} Controls + Move {0} Controls + + + + Resize {0} + Resize {0} + + + + Resize {0} Controls + Resize {0} Controls + + The serialization store is closed. New objects cannot be added to a closed store. The serialization store is closed. New objects cannot be added to a closed store. @@ -17,14 +47,54 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - Resize {0} - Resize {0} + + Format {0} components (alignment) + Format {0} components (alignment) - - Resize {0} Controls - Resize {0} Controls + + Align {0} components to grid + Align {0} components to grid + + + + Cut {0} Components + Cut {0} Components + + + + Delete {0} components + Delete {0} components + + + + An error occurred while processing this command.\r\n{0} + An error occurred while processing this command.\r\n{0} + + + + Format {0} components (spacing) + Format {0} components (spacing) + + + + Paste components + Paste components + + + + Size {0} components + Size {0} components + + + + Size {0} components to grid + Size {0} components to grid + + + + Unknown spacing command + Unknown spacing command @@ -47,6 +117,91 @@ Integer cannot be converted to a float. + + Align To &Grid + Align To &Grid + + + + &Bring To Front + &Bring To Front + + + + C&opy + C&opy + + + + &Cut + &Cut + + + + &Delete + &Delete + + + + &Document Outline + &Document Outline + + + + &Lock Controls + &Lock Controls + + + + &Paste + &Paste + + + + &Properties + &Properties + + + + &Select + &Select + + + + &Send To Back + &Send To Back + + + + ShowCheckMargin + ShowCheckMargin + + + + Toggles the ShowCheckMargin property + Toggles the ShowCheckMargin property + + + + ShowImageMargin + ShowImageMargin + + + + Toggles the ShowImageMargin property + Toggles the ShowImageMargin property + + + + View &Code + View &Code + + + + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + + The container cannot be disposed at design time. The container cannot be disposed at design time. @@ -77,9 +232,44 @@ The service {0} cannot be removed from the service container. + + Could not convert value '{0}' to the type '{1}'. + Could not convert value '{0}' to the type '{1}'. + + - Could not find method '{0}' - Could not find method '{0}' + Could not find method '{0}'. + Could not find method '{0}'. + + + + Could not find property '{0}' on '{1}'. + Could not find property '{0}' on '{1}'. + + + + {0} Tasks + {0} Tasks + + + + Error using the dropdown: {0} + Error using the dropdown: {0} + + + + Error invoking '{0}'. Details: {1} + Error invoking '{0}'. Details: {1} + + + + Error setting value '{0}' to property '{1}'. Details: {2} + Error setting value '{0}' to property '{1}'. Details: {2} + + + + Call to BeginDrag must succeed before calling drag functions. + Call to BeginDrag must succeed before calling drag functions. @@ -142,6 +332,16 @@ New components cannot be added while a designer is unloading. + + Inherited control + Inherited control + + + + Inherited control (Private) + Inherited control (Private) + + Optimized Code Generation 最佳化程式碼產生 @@ -247,11 +447,36 @@ .NET Component + + Drag {0} components + Drag {0} components + + + + Move {0} + Move {0} + + + + Move {0} components + Move {0} components + + This IDataObject doesn't support SetData. 此 IDataObject 不支援 SetData。 + + Size {0} + Size {0} + + + + Size {0} components + Size {0} components + + The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties. @@ -262,6 +487,16 @@ Read-Only + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. + + + + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}. + + This method/object is not implemented by design. 此方法/物件並非由設計實作。 @@ -272,6 +507,11 @@ 此平台不支援 WinForms Designer。 + + RTL_False + RTL_False + + You cannot create a new session because this serialization manager already has an active serialization session. You cannot create a new session because this serialization manager already has an active serialization session. @@ -377,6 +617,296 @@ The variable '{0}' is either undeclared or was never assigned. + + &About... + &About... + + + + &Contents + &Contents + + + + &Copy + &Copy + + + + Create Standard Menu + Create Standard Menu + + + + &Customize + &Customize + + + + Cu&t + Cu&t + + + + &Edit + &Edit + + + + E&xit + E&xit + + + + &File + &File + + + + &Help + &Help + + + + &Index + &Index + + + + &New + &New + + + + &Open + &Open + + + + &Options + &Options + + + + &Paste + &Paste + + + + &Print + &Print + + + + Print Pre&view + Print Pre&view + + + + &Redo + &Redo + + + + &Save + &Save + + + + Save &As + Save &As + + + + &Search + &Search + + + + Select &All + Select &All + + + + &Tools + &Tools + + + + &Undo + &Undo + + + + C&ut + C&ut + + + + He&lp + He&lp + + + + Dock: + Dock: + + + + Changes the Dock property + Changes the Dock property + + + + GripStyle: + GripStyle: + + + + Changes the GripStyle property + Changes the GripStyle property + + + + Layout && Appearance + Layout && Appearance + + + + RenderMode: + RenderMode: + + + + Changes the RenderMode property + Changes the RenderMode property + + + + Adding {0} Item + Adding {0} Item + + + + AllowItemReorder and AllowDrop cannot both be true. + AllowItemReorder and AllowDrop cannot both be true. + + + + ToolStrip New Item create Transaction. + ToolStrip New Item create Transaction. + + + + Embed in ToolStripContainer + Embed in ToolStripContainer + + + + Embeds the current ToolStrip in ToolStripContainer + Embeds the current ToolStrip in ToolStripContainer + + + + &Insert Standard Items + &Insert Standard Items + + + + Inserts standard items in the current ToolStrip + Inserts standard items in the current ToolStrip + + + + Type Here + Type Here + + + + Type Text for ToolStripMenuItem + Type Text for ToolStripMenuItem + + + + Add Menu Item + Add Menu Item + + + + Add ToolStripStatusLabel + Add ToolStripStatusLabel + + + + Add ToolStripButton + Add ToolStripButton + + + + New item selection + New item selection + + + + Adding Item + Adding Item + + + + Removing Item + Removing Item + + + + &Edit DropDownItems... + &Edit DropDownItems... + + + + ToolStrip MenuItem Insert in DropDown Transaction. + ToolStrip MenuItem Insert in DropDown Transaction. + + + + &Edit Items... + &Edit Items... + + + + Con&vert To + Con&vert To + + + + &Insert + &Insert + + + + Set I&mage... + Set I&mage... + + + + ToolStripItem Property Change Transaction. + ToolStripItem Property Change Transaction. + + + + ToolStripItem Morphing Transaction. + ToolStripItem Morphing Transaction. + + + + '{0}' + '{0}' + + + + Cannot add ToolStripSeparator to MenuStrip. + Cannot add ToolStripSeparator to MenuStrip. + + Argument should be a non-empty string. Argument should be a non-empty string. @@ -397,11 +927,31 @@ Data type {0} is not serializable. Items added to a property dictionary must be serializable. + + Auto Arrange Tray Icons + Auto Arrange Tray Icons + + + + Line Up Tray Icons + Line Up Tray Icons + + + + Show Large or Small Icons + Show Large or Small Icons + + Type '{0}' is not available in the target framework. Type '{0}' is not available in the target framework. + + Error + Error + + Add Component Add Component @@ -452,6 +1002,26 @@ Not implemented. + + <couldn't find resource string "WindowsFormsAddEvent"> + <couldn't find resource string "WindowsFormsAddEvent"> + + + + Horizontal center of {0} component(s) + Horizontal center of {0} component(s) + + + + Vertical center of {0} component(s) + Vertical center of {0} component(s) + + + + The Locked property determines if we can move or resize the control. + The Locked property determines if we can move or resize the control. + + - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/System.Windows.Forms.Design.csproj b/src/System.Windows.Forms.Design/src/System.Windows.Forms.Design.csproj index 0e554eb7b32..241b87fd116 100644 --- a/src/System.Windows.Forms.Design/src/System.Windows.Forms.Design.csproj +++ b/src/System.Windows.Forms.Design/src/System.Windows.Forms.Design.csproj @@ -10,7 +10,7 @@ true $(NoWarn);618 - $(DefineConstants);WINFORMS_DESIGN_NAMESPACE; + $(DefineConstants);WINFORMS_DESIGN_NAMESPACE;WINDOWS_FORMS_SWITCHES @@ -26,10 +26,13 @@ + + + @@ -38,6 +41,7 @@ + diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/ComponentDesigner.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/ComponentDesigner.cs index 25c4ef3abfe..7c38a0021fe 100644 --- a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/ComponentDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/ComponentDesigner.cs @@ -45,12 +45,11 @@ public virtual DesignerActionListCollection ActionLists /// public virtual ICollection AssociatedComponents { - get - { - return new IComponent[0]; - } + get => new IComponent[0]; } + internal virtual bool CanBeAssociatedWith(IDesigner parentDesigner) => true; + /// /// Gets or sets a value indicating whether or not this component is being inherited. /// @@ -456,6 +455,23 @@ public virtual void DoDefaultAction() } } + internal bool IsRootDesigner + { + get + { + Debug.Assert(_component != null, + "this.component needs to be set before this method is valid."); + + bool isRoot = false; + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null && _component == host.RootComponent) + { + isRoot = true; + } + return isRoot; + } + } + /// /// Initializes a new instance of the class using the specified component. /// diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionHeaderItem.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionHeaderItem.cs new file mode 100644 index 00000000000..029ac2137d5 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionHeaderItem.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.ComponentModel.Design +{ + public sealed class DesignerActionHeaderItem : DesignerActionTextItem + { + public DesignerActionHeaderItem(string displayName) : base(displayName, displayName) + { + } + + public DesignerActionHeaderItem(string displayName, string category) : base(displayName, category) + { + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionListsChangedEventArgs.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionListsChangedEventArgs.cs new file mode 100644 index 00000000000..54ef87e8bae --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionListsChangedEventArgs.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel.Design; + +namespace System.Windows.Forms.Design +{ + /// + /// This EventArgs class is used by the DesignerActionService to signify that there has been a change in DesignerActionLists (added or removed) on the related object. + /// + public class DesignerActionListsChangedEventArgs : EventArgs + { + private readonly object _relatedObject; + private readonly DesignerActionListCollection _actionLists; + private readonly DesignerActionListsChangedType _changeType; //type of change + + /// + /// Constructor that requires the object in question, the type of change and the remaining actionlists left for the object. on the related object. + /// + public DesignerActionListsChangedEventArgs(object relatedObject, DesignerActionListsChangedType changeType, DesignerActionListCollection actionLists) + { + _relatedObject = relatedObject; + _changeType = changeType; + _actionLists = actionLists; + } + + /// + /// The type of changed that caused the related event to be thrown. + /// + public DesignerActionListsChangedType ChangeType + { + get => _changeType; + } + + /// + /// The object this change is related to. + /// + public object RelatedObject + { + get => _relatedObject; + } + + /// + /// The remaining actionlists left for the related object. + /// + public DesignerActionListCollection ActionLists + { + get => _actionLists; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionListsChangedEventHandler.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionListsChangedEventHandler.cs new file mode 100644 index 00000000000..0c5e5b1fac0 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionListsChangedEventHandler.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Design +{ + /// + /// This event is thown by the DesignerActionListservice when a shortcut is either added or removed to/from the related object. + /// + [System.Runtime.InteropServices.ComVisible(true)] + public delegate void DesignerActionListsChangedEventHandler(object sender, DesignerActionListsChangedEventArgs e); +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionListsChangedType.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionListsChangedType.cs new file mode 100644 index 00000000000..7d4d4f8d577 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionListsChangedType.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Design +{ + /// + /// An enum that defines what time of action happend to the related object's DesignerActionLists collection. + /// + [Runtime.InteropServices.ComVisible(true)] + public enum DesignerActionListsChangedType + { + /// + /// Signifies that one or more DesignerActionList was added. + /// + ActionListsAdded, + /// + /// Signifies that one or more DesignerActionList was removed. + /// + ActionListsRemoved + + } +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs new file mode 100644 index 00000000000..5b3ec89386a --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs @@ -0,0 +1,3037 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Design; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Globalization; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; +using System.Windows.Forms.Design; +using System.Windows.Forms.VisualStyles; + +namespace System.ComponentModel.Design +{ + internal sealed class DesignerActionPanel : ContainerControl + { + public const string ExternDllGdi32 = "gdi32.dll"; + public const string ExternDllUser32 = "user32.dll"; + private static readonly object s_eventFormActivated = new object(); + private static readonly object s_eventFormDeactivate = new object(); + + private const int EditInputWidth = 150; // The static size of edit controls + private const int ListBoxMaximumHeight = 200; // The maximum height of a dropdown listbox + private const int MinimumWidth = 150; // The minimum overall width of the panel + private const int BottomPadding = 2; // Padding at the bottom of the panel + private const int TopPadding = 2; // Padding at the top of the panel + + private const int LineLeftMargin = 5; // Left padding for all lines + private const int LineRightMargin = 4; // Right padding for all lines + private const int LineVerticalPadding = 7; // Vertical padding between lines + private const int TextBoxTopPadding = 4; // Additional padding for top of textbox lines + private const int SeparatorHorizontalPadding = 3; // Left and right padding for separator lines + private const int TextBoxLineCenterMargin = 5; // Padding between the label region and editor region of a textbox line + private const int TextBoxLineInnerPadding = 1; // Padding within the editor region of a textbox line + + private const int EditorLineSwatchPadding = 1; // Padding for the swatch of an editor line + private const int EditorLineButtonPadding = 1; // Padding for the button of an editor line + private const int PanelHeaderVerticalPadding = 3; // Vertical padding within the header of the panel + private const int PanelHeaderHorizontalPadding = 5; // Horizontal padding within the header of the panel + + private const int TextBoxHeightFixup = 2; // Countereffects the fix for VSWhidbey 359726 - we relied on the broken behavior before + private CommandID[] _filteredCommandIDs; + private readonly ToolTip _toolTip; + private readonly List _lines; + private readonly List _lineYPositions; + private readonly List _lineHeights; + + private readonly Color _gradientLightColor = SystemColors.Control; + private readonly Color _gradientDarkColor = SystemColors.Control; + private readonly Color _titleBarColor = SystemColors.ActiveCaption; + private readonly Color _titleBarUnselectedColor = SystemColors.InactiveCaption; + private readonly Color _titleBarTextColor = SystemColors.ActiveCaptionText; + private readonly Color _separatorColor = SystemColors.ControlDark; + private readonly Color _borderColor = SystemColors.ActiveBorder; + private readonly Color _linkColor = SystemColors.HotTrack; + private readonly Color _activeLinkColor = SystemColors.HotTrack; + private readonly Color _labelForeColor = SystemColors.ControlText; + + private readonly IServiceProvider _serviceProvider; + private bool _inMethodInvoke; +#if MVWASSEMBLY + private bool _inPushingValue; +#endif + private bool _updatingTasks; + private bool _dropDownActive; + + public DesignerActionPanel(IServiceProvider serviceProvider) + { + SetStyle(ControlStyles.AllPaintingInWmPaint, true); + SetStyle(ControlStyles.Opaque, true); + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + SetStyle(ControlStyles.ResizeRedraw, true); + SetStyle(ControlStyles.UserPaint, true); + + _serviceProvider = serviceProvider; + _lines = new List(); + _lineHeights = new List(); + _lineYPositions = new List(); + _toolTip = new ToolTip(); + // Try to get the font from the IUIService, otherwise, use the default + IUIService uiService = (IUIService)ServiceProvider.GetService(typeof(IUIService)); + if (uiService != null) + { + Font = (Font)uiService.Styles["DialogFont"]; + if (uiService.Styles["VsColorPanelGradientDark"] is Color) + { + _gradientDarkColor = (Color)uiService.Styles["VsColorPanelGradientDark"]; + } + if (uiService.Styles["VsColorPanelGradientLight"] is Color) + { + _gradientLightColor = (Color)uiService.Styles["VsColorPanelGradientLight"]; + } + if (uiService.Styles["VsColorPanelHyperLink"] is Color) + { + _linkColor = (Color)uiService.Styles["VsColorPanelHyperLink"]; + } + if (uiService.Styles["VsColorPanelHyperLinkPressed"] is Color) + { + _activeLinkColor = (Color)uiService.Styles["VsColorPanelHyperLinkPressed"]; + } + if (uiService.Styles["VsColorPanelTitleBar"] is Color) + { + _titleBarColor = (Color)uiService.Styles["VsColorPanelTitleBar"]; + } + if (uiService.Styles["VsColorPanelTitleBarUnselected"] is Color) + { + _titleBarUnselectedColor = (Color)uiService.Styles["VsColorPanelTitleBarUnselected"]; + } + if (uiService.Styles["VsColorPanelTitleBarText"] is Color) + { + _titleBarTextColor = (Color)uiService.Styles["VsColorPanelTitleBarText"]; + } + if (uiService.Styles["VsColorPanelBorder"] is Color) + { + _borderColor = (Color)uiService.Styles["VsColorPanelBorder"]; + } + if (uiService.Styles["VsColorPanelSeparator"] is Color) + { + _separatorColor = (Color)uiService.Styles["VsColorPanelSeparator"]; + } + if (uiService.Styles["VsColorPanelText"] is Color) + { + _labelForeColor = (Color)uiService.Styles["VsColorPanelText"]; + } + } + MinimumSize = new Size(150, 0); + } + + public Color ActiveLinkColor + { + get => _activeLinkColor; + } + + public Color BorderColor + { + get => _borderColor; + } + + private bool DropDownActive + { + get => _dropDownActive; + } + + /// + /// Returns the list of commands that should be filtered by the form that hosts this panel. This is done so that these specific commands will not get passed on to VS, and can instead be handled by the panel itself. + /// + public CommandID[] FilteredCommandIDs + { + get + { + if (_filteredCommandIDs == null) + { + _filteredCommandIDs = new CommandID[] { + StandardCommands.Copy, + StandardCommands.Cut, + StandardCommands.Delete, + StandardCommands.F1Help, + StandardCommands.Paste, + StandardCommands.Redo, + StandardCommands.SelectAll, + StandardCommands.Undo, + MenuCommands.KeyCancel, + MenuCommands.KeyReverseCancel, + MenuCommands.KeyDefaultAction, + MenuCommands.KeyEnd, + MenuCommands.KeyHome, + MenuCommands.KeyMoveDown, + MenuCommands.KeyMoveLeft, + MenuCommands.KeyMoveRight, + MenuCommands.KeyMoveUp, + MenuCommands.KeyNudgeDown, + MenuCommands.KeyNudgeHeightDecrease, + MenuCommands.KeyNudgeHeightIncrease, + MenuCommands.KeyNudgeLeft, + MenuCommands.KeyNudgeRight, + MenuCommands.KeyNudgeUp, + MenuCommands.KeyNudgeWidthDecrease, + MenuCommands.KeyNudgeWidthIncrease, + MenuCommands.KeySizeHeightDecrease, + MenuCommands.KeySizeHeightIncrease, + MenuCommands.KeySizeWidthDecrease, + MenuCommands.KeySizeWidthIncrease, + MenuCommands.KeySelectNext, + MenuCommands.KeySelectPrevious, + MenuCommands.KeyShiftEnd, + MenuCommands.KeyShiftHome, + }; + } + return _filteredCommandIDs; + } + } + + /// + /// Gets the Line that currently has input focus. + /// + private Line FocusedLine + { + get + { + Control activeControl = ActiveControl; + if (activeControl != null) + { + return activeControl.Tag as Line; + } + return null; + } + } + + public Color GradientDarkColor + { + get => _gradientDarkColor; + } + + public Color GradientLightColor + { + get => _gradientLightColor; + } + + public bool InMethodInvoke + { + get => _inMethodInvoke; + internal set => _inMethodInvoke = value; + } + +#if MVWASSEMBLY + public bool InPushingValue { + get { + return _inPushingValue; + } + internal set { + _inPushingValue = value; + } + } +#endif + + public Color LinkColor + { + get => _linkColor; + } + + public Color SeparatorColor + { + get => _separatorColor; + } + + private IServiceProvider ServiceProvider + { + get => _serviceProvider; + } + + public Color TitleBarColor + { + get => _titleBarColor; + } + + public Color TitleBarTextColor + { + get => _titleBarTextColor; + } + + public Color TitleBarUnselectedColor + { + get => _titleBarUnselectedColor; + } + + public Color LabelForeColor + { + get => _labelForeColor; + } + + /// + /// Helper event so that Lines can be notified of this event. + /// + private event EventHandler FormActivated + { + add => Events.AddHandler(s_eventFormActivated, value); + remove => Events.RemoveHandler(s_eventFormActivated, value); + } + + /// + /// Helper event so that Lines can be notified of this event. + /// + private event EventHandler FormDeactivate + { + add => Events.AddHandler(s_eventFormDeactivate, value); + remove => Events.RemoveHandler(s_eventFormDeactivate, value); + } + + private void AddToCategories(LineInfo lineInfo, ListDictionary categories) + { + string categoryName = lineInfo.Item.Category; + if (categoryName == null) + { + categoryName = string.Empty; + } + + ListDictionary category = (ListDictionary)categories[categoryName]; + if (category == null) + { + category = new ListDictionary(); + categories.Add(categoryName, category); + } + + List categoryList = (List)category[lineInfo.List]; + if (categoryList == null) + { + categoryList = new List(); + category.Add(lineInfo.List, categoryList); + } + categoryList.Add(lineInfo); + } + + /// + /// Computes the best possible location (in desktop coordinates) to display the panel, given the size of the panel and the position of its anchor + /// + public static Point ComputePreferredDesktopLocation(Rectangle rectangleAnchor, Size sizePanel, out DockStyle edgeToDock) + { + Rectangle rectScreen = Screen.FromPoint(rectangleAnchor.Location).WorkingArea; + // Determine where we can draw the panel to minimize clipping. Start with the most preferred position, i.e. bottom-right of anchor For the purposes of computing the flags below, assume the anchor to be small enough to ignore its size. + bool fRightOfAnchor = true; + bool fAlignToScreenLeft = false; + + // if the panel is too wide, try flipping to left or aligning to screen left + if (rectangleAnchor.Right + sizePanel.Width > rectScreen.Right) + { // no room at right, try at left of anchor + fRightOfAnchor = false; + if (rectangleAnchor.Left - sizePanel.Width < rectScreen.Left) + { // no room at left, either + fAlignToScreenLeft = true; + } + } + + bool fBelowAnchor = (fRightOfAnchor ? true : false); + bool fAlignToScreenTop = false; + if (fBelowAnchor) + { + // if the panel is too tall, try flipping to top or aligning to screen top + if (rectangleAnchor.Bottom + sizePanel.Height > rectScreen.Bottom) + { // no room at bottom, try at top of anchor + fBelowAnchor = false; + if (rectangleAnchor.Top - sizePanel.Height < rectScreen.Top) + { // no room at top, either + fAlignToScreenTop = true; + } + } + } + else + { + // if the panel is too tall, try flipping to bottom or aligning to screen top + if (rectangleAnchor.Top - sizePanel.Height < rectScreen.Top) + { // no room at top, try at bottom of anchor + fBelowAnchor = true; + if (rectangleAnchor.Bottom + sizePanel.Height > rectScreen.Bottom) + { // no room at bottom, either + fAlignToScreenTop = true; + } + } + } + + // The flags give us a total of nine possible positions - {LeftOfAnchor, RightOfAnchor, AlignToScreenLeft} X {AboveAnchor, BelowAnchor, AlignToScreenTop} + // Out of these, we rule out one combination (AlignToScreenLeft, AlignToScreenTop) because this does not guarantee the alignment of an anchor edge with that of the panel edge + if (fAlignToScreenTop) + { + fAlignToScreenLeft = false; + } + + int x = 0, y = 0; + const int EDGE_SPACE = 0; + edgeToDock = DockStyle.None; + + // Compute the actual position now, based on the flags above, and taking the anchor size into account. + if (fAlignToScreenLeft && fBelowAnchor) + { + x = rectScreen.Left; + y = rectangleAnchor.Bottom + EDGE_SPACE; + edgeToDock = DockStyle.Bottom; + } + else if (fAlignToScreenLeft && !fBelowAnchor) + { + x = rectScreen.Left; + y = rectangleAnchor.Top - sizePanel.Height - EDGE_SPACE; + edgeToDock = DockStyle.Top; + } + else if (fRightOfAnchor && fAlignToScreenTop) + { + x = rectangleAnchor.Right + EDGE_SPACE; + y = rectScreen.Top; + edgeToDock = DockStyle.Right; + } + else if (fRightOfAnchor && fBelowAnchor) + { + x = rectangleAnchor.Right + EDGE_SPACE; + y = rectangleAnchor.Top; + edgeToDock = DockStyle.Right; + } + else if (fRightOfAnchor && !fBelowAnchor) + { + x = rectangleAnchor.Right + EDGE_SPACE; + y = rectangleAnchor.Bottom - sizePanel.Height; + edgeToDock = DockStyle.Right; + } + else if (!fRightOfAnchor && fAlignToScreenTop) + { + x = rectangleAnchor.Left - sizePanel.Width - EDGE_SPACE; + y = rectScreen.Top; + edgeToDock = DockStyle.Left; + } + else if (!fRightOfAnchor && fBelowAnchor) + { + x = rectangleAnchor.Left - sizePanel.Width - EDGE_SPACE; + y = rectangleAnchor.Top; + edgeToDock = DockStyle.Left; + } + else if (!fRightOfAnchor && !fBelowAnchor) + { + x = rectangleAnchor.Right - sizePanel.Width; + y = rectangleAnchor.Top - sizePanel.Height - EDGE_SPACE; + edgeToDock = DockStyle.Top; + } + else + { + Debug.Assert(false); // should never get here + } + return new Point(x, y); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _toolTip.Dispose(); + } + base.Dispose(disposing); + } + + private Size DoLayout(Size proposedSize, bool measureOnly) + { + // REVIEW: Is this a WinForms bug? This shouldn't be called if we're disposing since no one should care about layout + if (Disposing || IsDisposed) + { + return Size.Empty; + } + + int panelWidth = MinimumWidth; + int yPos = 0; + SuspendLayout(); + try + { + // Clear cached calculated information + _lineYPositions.Clear(); + _lineHeights.Clear(); + + // Layout each line + for (int i = 0; i < _lines.Count; i++) + { + Line line = _lines[i]; + _lineYPositions.Add(yPos); + Size size = line.LayoutControls(yPos, proposedSize.Width, measureOnly); + panelWidth = Math.Max(panelWidth, size.Width); + _lineHeights.Add(size.Height); + yPos += size.Height; + } + } + finally + { + ResumeLayout(!measureOnly); + } + return new Size(panelWidth, yPos + BottomPadding); + } + + public override Size GetPreferredSize(Size proposedSize) + { + // REVIEW: WinForms calls this inside of PerformLayout() only in DEBUG code.From the comment it looks like it's calling it to verify their own cached preferred size, so we just ignore this call. + if (proposedSize.IsEmpty) + { + return proposedSize; + } + return DoLayout(proposedSize, true); + } + + private static bool IsReadOnlyProperty(PropertyDescriptor pd) + { + if (pd.IsReadOnly) + { + return true; + } + return (pd.ComponentType.GetProperty(pd.Name).GetSetMethod() == null); + } + + protected override void OnFontChanged(EventArgs e) + { + base.OnFontChanged(e); + UpdateEditXPos(); + // REVIEW: How do we notify Lines that the font has changed? + } + + private void OnFormActivated(object sender, EventArgs e) + { + ((EventHandler)Events[s_eventFormActivated])?.Invoke(sender, e); + } + + private void OnFormClosing(object sender, CancelEventArgs e) + { + if (!e.Cancel && TopLevelControl != null) + { + Debug.Assert(TopLevelControl is Form, "DesignerActionPanel must be hosted on a Form."); + Form form = (Form)TopLevelControl; + if (form != null) + { + form.Activated -= new EventHandler(OnFormActivated); + form.Deactivate -= new EventHandler(OnFormDeactivate); + form.Closing -= new CancelEventHandler(OnFormClosing); + } + } + } + + private void OnFormDeactivate(object sender, EventArgs e) + { + ((EventHandler)Events[s_eventFormDeactivate])?.Invoke(sender, e); + } + + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + if (TopLevelControl is Form form) + { + form.Activated += new EventHandler(OnFormActivated); + form.Deactivate += new EventHandler(OnFormDeactivate); + form.Closing += new CancelEventHandler(OnFormClosing); + } + } + + protected override void OnLayout(LayoutEventArgs levent) + { + if (_updatingTasks) + { + return; + } + + DoLayout(Size, false); + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + if (_updatingTasks) + { + return; + } + + Rectangle rect = Bounds; + if (RightToLeft == RightToLeft.Yes) + { + using (LinearGradientBrush gradientBrush = new LinearGradientBrush(rect, GradientDarkColor, GradientLightColor, LinearGradientMode.Horizontal)) + { + e.Graphics.FillRectangle(gradientBrush, ClientRectangle); + } + } + else + { + using (LinearGradientBrush gradientBrush = new LinearGradientBrush(rect, GradientLightColor, GradientDarkColor, LinearGradientMode.Horizontal)) + { + e.Graphics.FillRectangle(gradientBrush, ClientRectangle); + } + } + + using (Pen borderPen = new Pen(BorderColor)) + { + e.Graphics.DrawRectangle(borderPen, new Rectangle(0, 0, Width - 1, Height - 1)); + } + + Rectangle originalClip = e.ClipRectangle; + // Determine the first line index to paint + int index = 0; + while ((index < (_lineYPositions.Count - 1)) && (_lineYPositions[index + 1] <= originalClip.Top)) + { + index++; + } + + Graphics g = e.Graphics; + for (int i = index; i < _lineYPositions.Count; i++) + { + Line line = _lines[i]; + int yPos = _lineYPositions[i]; + int lineHeight = _lineHeights[i]; + int lineWidth = Width; + // Set the clip rectangle so the lines can't mess with each other + g.SetClip(new Rectangle(0, yPos, lineWidth, lineHeight)); + + // Normalize the paint coordinates + g.TranslateTransform(0, yPos); + line.PaintLine(g, lineWidth, lineHeight); + g.ResetTransform(); + // Stop if we've painted all the lines in the clip rectangle + if (yPos + lineHeight > originalClip.Bottom) + { + break; + } + } + } + + protected override void OnRightToLeftChanged(EventArgs e) + { + base.OnRightToLeftChanged(e); + PerformLayout(); + } + + protected override bool ProcessDialogKey(Keys keyData) + { + // TODO: RightToLeft management for left/right arrow keys (from old DesignerActionPanel) + Line focusedLine = FocusedLine; + if (focusedLine != null) + { + if (focusedLine.ProcessDialogKey(keyData)) + { + return true; + } + } + return base.ProcessDialogKey(keyData); + } + + // we want to loop + protected override bool ProcessTabKey(bool forward) + { + return (SelectNextControl(ActiveControl, forward, true, true, true)); + } + + private void ProcessLists(DesignerActionListCollection lists, ListDictionary categories) + { + if (lists == null) + { + return; + } + foreach (DesignerActionList list in lists) + { + if (list != null) + { + IEnumerable items = list.GetSortedActionItems(); + if (items != null) + { + foreach (DesignerActionItem item in items) + { + if (item == null) + { + continue; + } + LineInfo lineInfo = ProcessTaskItem(list, item); + if (lineInfo == null) + { + continue; + } + AddToCategories(lineInfo, categories); + // Process lists from related component + IComponent relatedComponent = null; + if (item is DesignerActionPropertyItem propItem) + { + relatedComponent = propItem.RelatedComponent; + } + else + { + if (item is DesignerActionMethodItem methodItem) + { + relatedComponent = methodItem.RelatedComponent; + } + } + if (relatedComponent != null) + { + IEnumerable relatedLineInfos = ProcessRelatedTaskItems(relatedComponent); + if (relatedLineInfos != null) + { + foreach (LineInfo relatedLineInfo in relatedLineInfos) + { + AddToCategories(relatedLineInfo, categories); + } + } + } + } + } + } + } + } + + private IEnumerable ProcessRelatedTaskItems(IComponent relatedComponent) + { + // Add the related tasks + Debug.Assert(relatedComponent != null); + DesignerActionListCollection relatedLists = null; + DesignerActionService actionService = (DesignerActionService)ServiceProvider.GetService(typeof(DesignerActionService)); + if (actionService != null) + { + relatedLists = actionService.GetComponentActions(relatedComponent); + } + else + { + // Try to use the component's service provider if it exists so that we end up getting the right IDesignerHost. + IServiceProvider serviceProvider = relatedComponent.Site; + if (serviceProvider == null) + { + serviceProvider = ServiceProvider; + } + IDesignerHost host = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost)); + if (host != null) + { + if (host.GetDesigner(relatedComponent) is ComponentDesigner componentDesigner) + { + relatedLists = componentDesigner.ActionLists; + } + } + } + + List lineInfos = new List(); + + if (relatedLists != null) + { + foreach (DesignerActionList relatedList in relatedLists) + { + if (relatedList != null) + { + IEnumerable items = relatedList.GetSortedActionItems(); + if (items != null) + { + foreach (DesignerActionItem relatedItem in items) + { + if (relatedItem != null) + { + if (relatedItem.AllowAssociate) + { + LineInfo lineInfo = ProcessTaskItem(relatedList, relatedItem); + if (lineInfo != null) + { + lineInfos.Add(lineInfo); + } + } + } + } + } + } + } + } + return lineInfos; + } + + private LineInfo ProcessTaskItem(DesignerActionList list, DesignerActionItem item) + { + Line newLine; + if (item is DesignerActionMethodItem) + { + newLine = new MethodLine(_serviceProvider, this); + } + else if (item is DesignerActionPropertyItem pti) + { + PropertyDescriptor pd = TypeDescriptor.GetProperties(list)[pti.MemberName]; + if (pd == null) + { + throw new InvalidOperationException(string.Format(SR.DesignerActionPanel_CouldNotFindProperty, pti.MemberName, list.GetType().FullName)); + } + + TypeDescriptorContext context = new TypeDescriptorContext(_serviceProvider, pd, list); + UITypeEditor editor = (UITypeEditor)pd.GetEditor(typeof(UITypeEditor)); + bool standardValuesSupported = pd.Converter.GetStandardValuesSupported(context); + if (editor == null) + { + if (pd.PropertyType == typeof(bool)) + { + if (IsReadOnlyProperty(pd)) + { + newLine = new TextBoxPropertyLine(_serviceProvider, this); + } + else + { + newLine = new CheckBoxPropertyLine(_serviceProvider, this); + } + } + else if (standardValuesSupported) + { + newLine = new EditorPropertyLine(_serviceProvider, this); + } + else + { + newLine = new TextBoxPropertyLine(_serviceProvider, this); + } + } + else + { + newLine = new EditorPropertyLine(_serviceProvider, this); + } + } + else if (item is DesignerActionTextItem) + { + if (item is DesignerActionHeaderItem) + { + newLine = new HeaderLine(_serviceProvider, this); + } + else + { + newLine = new TextLine(_serviceProvider, this); + } + } + else + { + // Ignore unknown items + return null; + } + return new LineInfo(list, item, newLine); + } + + private void SetDropDownActive(bool active) + { + _dropDownActive = active; + } + + private void ShowError(string errorMessage) + { + IUIService uiService = (IUIService)ServiceProvider.GetService(typeof(IUIService)); + if (uiService != null) + { + uiService.ShowError(errorMessage); + } + else + { + MessageBoxOptions options = 0; + if (SR.RTL != "RTL_False") + { + options = (MessageBoxOptions.RightAlign | MessageBoxOptions.RtlReading); + } + MessageBox.Show(this, errorMessage, string.Format(SR.UIServiceHelper_ErrorCaption), MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, options); + } + } + + /// + /// Strips out ampersands used for mnemonics so that they don't show up in the rendering. + /// - Convert "&&" to "&" + /// - Convert "&x" to "x" + /// - An ampersand by itself at the end of a string is displayed as-is + /// + private static string StripAmpersands(string s) + { + if (string.IsNullOrEmpty(s)) + { + return string.Empty; + } + StringBuilder result = new StringBuilder(s.Length); + for (int i = 0; i < s.Length; i++) + { + if (s[i] == '&') + { + // Skip over the ampersand + i++; + if (i == s.Length) + { + // If we're at the last character just add the ampersand and stop + result.Append('&'); + break; + } + } + result.Append(s[i]); + } + return result.ToString(); + } + + private void UpdateEditXPos() + { + // Find the correct edit control position + int editXPos = 0; + for (int i = 0; i < _lines.Count; i++) + { + if (_lines[i] is TextBoxPropertyLine line) + { + editXPos = Math.Max(editXPos, ((TextBoxPropertyLine)line).GetEditRegionXPos()); + } + } + + // Make all the edit controls line up + for (int i = 0; i < _lines.Count; i++) + { + if (_lines[i] is TextBoxPropertyLine line) + { + line.SetEditRegionXPos(editXPos); + } + } + } + + public void UpdateTasks(DesignerActionListCollection actionLists, DesignerActionListCollection serviceActionLists, string title, string subtitle) + { + _updatingTasks = true; + SuspendLayout(); + try + { + AccessibleName = title; + AccessibleDescription = subtitle; + + // Store the focus state + string focusId = string.Empty; + Line focusedLine = FocusedLine; + if (focusedLine != null) + { + focusId = focusedLine.FocusId; + } + + // Merge the categories from the lists and create controls for each of the items + ListDictionary categories = new ListDictionary(); + ProcessLists(actionLists, categories); + ProcessLists(serviceActionLists, categories); + // Create a flat list of lines w/ separators + List newLines = new List + { + // Always add a special line for the header + new LineInfo(null, new DesignerActionPanelHeaderItem(title, subtitle), new PanelHeaderLine(_serviceProvider, this)) + }; + int categoriesIndex = 0; + foreach (ListDictionary category in categories.Values) + { + int categoryIndex = 0; + foreach (List categoryList in category.Values) + { + for (int i = 0; i < categoryList.Count; i++) + { + newLines.Add(categoryList[i]); + } + + categoryIndex++; + // Add a sub-separator + if (categoryIndex < category.Count) + { + newLines.Add(new LineInfo(null, null, new SeparatorLine(_serviceProvider, this, true))); + } + } + + categoriesIndex++; + // Add a separator + if (categoriesIndex < categories.Count) + { + newLines.Add(new LineInfo(null, null, new SeparatorLine(_serviceProvider, this))); + } + } + + // Now try to update similar lines + int currentTabIndex = 0; + for (int i = 0; i < newLines.Count; i++) + { + LineInfo newLineInfo = newLines[i]; + Line newLine = newLineInfo.Line; + // See if we can update an old line + bool updated = false; + if (i < _lines.Count) + { + Line oldLine = _lines[i]; + + if (oldLine.GetType() == newLine.GetType()) + { + oldLine.UpdateActionItem(newLineInfo.List, newLineInfo.Item, _toolTip, ref currentTabIndex); + updated = true; + } + else + { + oldLine.RemoveControls(Controls); + _lines.RemoveAt(i); + } + } + + if (!updated) + { + // Add the new controls + List newControlList = newLine.GetControls(); + Control[] controls = new Control[newControlList.Count]; + newControlList.CopyTo(controls); + Controls.AddRange(controls); + + newLine.UpdateActionItem(newLineInfo.List, newLineInfo.Item, _toolTip, ref currentTabIndex); + _lines.Insert(i, newLine); + } + } + + // Remove any excess lines + for (int i = _lines.Count - 1; i >= newLines.Count; i--) + { + Line excessLine = _lines[i]; + excessLine.RemoveControls(Controls); + _lines.RemoveAt(i); + } + + // Restore focus + if (!string.IsNullOrEmpty(focusId)) + { + foreach (Line line in _lines) + { + if (string.Equals(line.FocusId, focusId, StringComparison.Ordinal)) + { + line.Focus(); + } + } + } + } + finally + { + UpdateEditXPos(); + _updatingTasks = false; + // REVIEW: We should rely on the caller to actually perform layout since it our scenarios, the entire right pane will have to be layed out + // Actually, we do want to resume layout since invalidation causes an OnPaint, and OnPaint relies on everything being layed out already + ResumeLayout(true); + } + Invalidate(); + } + + private class LineInfo + { + public Line Line; + public DesignerActionItem Item; + public DesignerActionList List; + + public LineInfo(DesignerActionList list, DesignerActionItem item, Line line) + { + Debug.Assert(line != null); + Line = line; + Item = item; + List = list; + } + } + + internal sealed class TypeDescriptorContext : ITypeDescriptorContext + { + private readonly IServiceProvider _serviceProvider; + private readonly PropertyDescriptor _propDesc; + private readonly object _instance; + + public TypeDescriptorContext(IServiceProvider serviceProvider, PropertyDescriptor propDesc, object instance) + { + _serviceProvider = serviceProvider; + _propDesc = propDesc; + _instance = instance; + } + + private IComponentChangeService ComponentChangeService + { + get => (IComponentChangeService)_serviceProvider.GetService(typeof(IComponentChangeService)); + } + + public IContainer Container + { + get => (IContainer)_serviceProvider.GetService(typeof(IContainer)); + } + + public object Instance + { + get => _instance; + } + + public PropertyDescriptor PropertyDescriptor + { + get => _propDesc; + } + + public object GetService(Type serviceType) => _serviceProvider.GetService(serviceType); + + public bool OnComponentChanging() + { + if (ComponentChangeService != null) + { + try + { + ComponentChangeService.OnComponentChanging(_instance, _propDesc); + } + catch (CheckoutException ce) + { + if (ce == CheckoutException.Canceled) + { + return false; + } + throw ce; + } + } + return true; + } + + public void OnComponentChanged() + { + if (ComponentChangeService != null) + { + ComponentChangeService.OnComponentChanged(_instance, _propDesc, null, null); + } + } + } + + private abstract class Line + { + private readonly DesignerActionPanel _actionPanel; + private List _addedControls; + private readonly IServiceProvider _serviceProvider; + + public Line(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) + { + _serviceProvider = serviceProvider; + _actionPanel = actionPanel ?? throw new ArgumentNullException("actionPanel"); + } + + protected DesignerActionPanel ActionPanel + { + get => _actionPanel; + } + + public abstract string FocusId + { + get; + } + + protected IServiceProvider ServiceProvider + { + get => _serviceProvider; + } + + protected abstract void AddControls(List controls); + + internal List GetControls() + { + _addedControls = new List(); + AddControls(_addedControls); + // Tag all the controls with the Line so we know who owns it + foreach (Control c in _addedControls) + { + c.Tag = this; + } + return _addedControls; + } + + public abstract void Focus(); + + public abstract Size LayoutControls(int top, int width, bool measureOnly); + + public virtual void PaintLine(Graphics g, int lineWidth, int lineHeight) + { + } + + protected internal virtual bool ProcessDialogKey(Keys keyData) => false; + + internal void RemoveControls(Control.ControlCollection controls) + { + for (int i = 0; i < _addedControls.Count; i++) + { + Control c = _addedControls[i]; + c.Tag = null; + controls.Remove(c); + } + } + + internal abstract void UpdateActionItem(DesignerActionList actionList, DesignerActionItem actionItem, ToolTip toolTip, ref int currentTabIndex); + } + + private sealed class DesignerActionPanelHeaderItem : DesignerActionItem + { + private readonly string _subtitle; + + public DesignerActionPanelHeaderItem(string title, string subtitle) : base(title, null, null) + { + _subtitle = subtitle; + } + + public string Subtitle + { + get => _subtitle; + } + } + + private sealed class PanelHeaderLine : Line + { + private DesignerActionList _actionList; + private DesignerActionPanelHeaderItem _panelHeaderItem; + private Label _titleLabel; + private Label _subtitleLabel; + private bool _formActive; + + public PanelHeaderLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) : base(serviceProvider, actionPanel) + { + actionPanel.FontChanged += new EventHandler(OnParentControlFontChanged); + } + + public sealed override string FocusId + { + get => string.Empty; + } + + protected override void AddControls(List controls) + { + _titleLabel = new Label + { + BackColor = Color.Transparent, + ForeColor = ActionPanel.TitleBarTextColor, + TextAlign = Drawing.ContentAlignment.MiddleLeft, + UseMnemonic = false + }; + + _subtitleLabel = new Label + { + BackColor = Color.Transparent, + ForeColor = ActionPanel.TitleBarTextColor, + TextAlign = Drawing.ContentAlignment.MiddleLeft, + UseMnemonic = false + }; + + controls.Add(_titleLabel); + controls.Add(_subtitleLabel); + // TODO: Need to figure out how to unhook these events. Perhaps have Initialize() and Cleanup() methods. + ActionPanel.FormActivated += new EventHandler(OnFormActivated); + ActionPanel.FormDeactivate += new EventHandler(OnFormDeactivate); + } + + public sealed override void Focus() + { + Debug.Fail("Should never try to focus a PanelHeaderLine"); + } + + public override Size LayoutControls(int top, int width, bool measureOnly) + { + Size titleSize = _titleLabel.GetPreferredSize(new Size(int.MaxValue, int.MaxValue)); + Size subtitleSize = Size.Empty; + if (!string.IsNullOrEmpty(_panelHeaderItem.Subtitle)) + { + subtitleSize = _subtitleLabel.GetPreferredSize(new Size(int.MaxValue, int.MaxValue)); + } + + if (!measureOnly) + { + _titleLabel.Location = new Point(LineLeftMargin, top + PanelHeaderVerticalPadding); + _titleLabel.Size = titleSize; + _subtitleLabel.Location = new Point(LineLeftMargin, top + PanelHeaderVerticalPadding * 2 + titleSize.Height); + _subtitleLabel.Size = subtitleSize; + } + int newWidth = Math.Max(titleSize.Width, subtitleSize.Width) + 2 * PanelHeaderHorizontalPadding; + int newHeight = (subtitleSize.IsEmpty ? (titleSize.Height + 2 * PanelHeaderVerticalPadding) : (titleSize.Height + subtitleSize.Height + 3 * PanelHeaderVerticalPadding)); + return new Size(newWidth + 2, newHeight + 1); + } + + private void OnFormActivated(object sender, EventArgs e) + { + // TODO: Figure out better rect + _formActive = true; + ActionPanel.Invalidate(); + //ActionPanel.Invalidate(new Rectangle(EditRegionLocation, EditRegionSize), false); + } + + private void OnFormDeactivate(object sender, EventArgs e) + { + // TODO: Figure out better rect + _formActive = false; + ActionPanel.Invalidate(); + } + + private void OnParentControlFontChanged(object sender, EventArgs e) + { + if (_titleLabel != null && _subtitleLabel != null) + { + _titleLabel.Font = new Font(ActionPanel.Font, FontStyle.Bold); + _subtitleLabel.Font = ActionPanel.Font; + } + } + + public override void PaintLine(Graphics g, int lineWidth, int lineHeight) + { + Color backColor = (_formActive || ActionPanel.DropDownActive) ? ActionPanel.TitleBarColor : ActionPanel.TitleBarUnselectedColor; + using (SolidBrush b = new SolidBrush(backColor)) + { + g.FillRectangle(b, 1, 1, lineWidth - 2, lineHeight - 1); + } + + // Paint a line under the title label + using (Pen p = new Pen(ActionPanel.BorderColor)) + { + g.DrawLine(p, 0, lineHeight - 1, lineWidth, lineHeight - 1); + } + } + + internal override void UpdateActionItem(DesignerActionList actionList, DesignerActionItem actionItem, ToolTip toolTip, ref int currentTabIndex) + { + _actionList = actionList; + _panelHeaderItem = (DesignerActionPanelHeaderItem)actionItem; + _titleLabel.Text = _panelHeaderItem.DisplayName; + _titleLabel.TabIndex = currentTabIndex++; + _subtitleLabel.Text = _panelHeaderItem.Subtitle; + _subtitleLabel.TabIndex = currentTabIndex++; + _subtitleLabel.Visible = (_subtitleLabel.Text.Length != 0); + // Force the font to update + OnParentControlFontChanged(null, EventArgs.Empty); + } + } + + private sealed class MethodLine : Line + { + private DesignerActionList _actionList; + private DesignerActionMethodItem _methodItem; + private MethodItemLinkLabel _linkLabel; + public MethodLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) : base(serviceProvider, actionPanel) + { + } + + public sealed override string FocusId + { + get => "METHOD:" + _actionList.GetType().FullName + "." + _methodItem.MemberName; + } + + protected override void AddControls(List controls) + { + _linkLabel = new MethodItemLinkLabel + { + ActiveLinkColor = ActionPanel.ActiveLinkColor, + AutoSize = false, + BackColor = Color.Transparent, + LinkBehavior = LinkBehavior.HoverUnderline, + LinkColor = ActionPanel.LinkColor, + TextAlign = Drawing.ContentAlignment.MiddleLeft, + UseMnemonic = false, + VisitedLinkColor = ActionPanel.LinkColor + }; + _linkLabel.LinkClicked += new LinkLabelLinkClickedEventHandler(OnLinkLabelLinkClicked); + controls.Add(_linkLabel); + } + + public sealed override void Focus() + { + _linkLabel.Focus(); + } + + public override Size LayoutControls(int top, int width, bool measureOnly) + { + Size linkLabelSize = _linkLabel.GetPreferredSize(new Size(int.MaxValue, int.MaxValue)); + if (!measureOnly) + { + _linkLabel.Location = new Point(LineLeftMargin, top + LineVerticalPadding / 2); + _linkLabel.Size = linkLabelSize; + } + return linkLabelSize + new Size(LineLeftMargin + LineRightMargin, LineVerticalPadding); + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + private void OnLinkLabelLinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + Debug.Assert(!ActionPanel.InMethodInvoke, "Nested method invocation"); + ActionPanel.InMethodInvoke = true; + try + { + _methodItem.Invoke(); + } + catch (Exception ex) + { + if (ex is TargetInvocationException) + { + ex = ex.InnerException; + } + //NOTE: We had code to rethrow if this was one of [NullReferenceException, StackOverflowException, OutOfMemoryException, + //ThreadAbortException]. Removing this rethrow. StackOverflow and ThreadAbort can't be meaningfully caught, and + //NullRef and OutOfMemory really shouldn't be caught. Out of these, OOM is the most correct one to call, but OOM is + //thrown by GDI+ for pretty much any problem, so isn't reliable as an actual indicator that you're out of memory. If + //you really are out of memory, it's very likely you'll get another OOM shortly. + ActionPanel.ShowError(string.Format(SR.DesignerActionPanel_ErrorInvokingAction, _methodItem.DisplayName, Environment.NewLine + ex.Message)); + } + finally + { + ActionPanel.InMethodInvoke = false; + } + } + + internal override void UpdateActionItem(DesignerActionList actionList, DesignerActionItem actionItem, ToolTip toolTip, ref int currentTabIndex) + { + _actionList = actionList; + _methodItem = (DesignerActionMethodItem)actionItem; + toolTip.SetToolTip(_linkLabel, _methodItem.Description); + _linkLabel.Text = StripAmpersands(_methodItem.DisplayName); + _linkLabel.AccessibleDescription = actionItem.Description; + _linkLabel.TabIndex = currentTabIndex++; + } + + private sealed class MethodItemLinkLabel : LinkLabel + { + protected override bool ProcessDialogKey(Keys keyData) + { + if ((keyData & Keys.Control) == Keys.Control) + { + Keys keyCode = keyData & Keys.KeyCode; + switch (keyCode) + { + case Keys.Tab: + // We specifically ignore Ctrl+Tab because it prevents the window switcher dialog from showing up in VS. Normally the key combination is only needed when a LinkLabel contains multiple links, but that can't happen inside the DesignerActionPanel. + return false; + } + } + return base.ProcessDialogKey(keyData); + } + } + } + + private abstract class PropertyLine : Line + { + private DesignerActionList _actionList; + private DesignerActionPropertyItem _propertyItem; + private object _value; + private bool _pushingValue; + private PropertyDescriptor _propDesc; + private ITypeDescriptorContext _typeDescriptorContext; + + public PropertyLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) : base(serviceProvider, actionPanel) + { + } + + public sealed override string FocusId + { + get => "PROPERTY:" + _actionList.GetType().FullName + "." + _propertyItem.MemberName; + } + + protected PropertyDescriptor PropertyDescriptor + { + get + { + if (_propDesc == null) + { + _propDesc = TypeDescriptor.GetProperties(_actionList)[_propertyItem.MemberName]; + } + return _propDesc; + } + } + + protected DesignerActionPropertyItem PropertyItem + { + get => _propertyItem; + } + + protected ITypeDescriptorContext TypeDescriptorContext + { + get + { + if (_typeDescriptorContext == null) + { + _typeDescriptorContext = new TypeDescriptorContext(ServiceProvider, PropertyDescriptor, _actionList); + } + return _typeDescriptorContext; + } + } + + protected object Value + { + get => _value; + } + + protected abstract void OnPropertyTaskItemUpdated(ToolTip toolTip, ref int currentTabIndex); + + protected abstract void OnValueChanged(); + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + protected void SetValue(object newValue) + { + if (_pushingValue || ActionPanel.DropDownActive) + { + return; + } + _pushingValue = true; + +#if MVWASSEMBLY + ActionPanel.InPushingValue = true; +#endif + try + { + // Only push the change if the values are different + if (newValue != null) + { + Type valueType = newValue.GetType(); + // If it's not assignable, try to convert it + if (!PropertyDescriptor.PropertyType.IsAssignableFrom(valueType)) + { + if (PropertyDescriptor.Converter != null) + { + // If we can't convert it, show an error + if (!PropertyDescriptor.Converter.CanConvertFrom(_typeDescriptorContext, valueType)) + { + ActionPanel.ShowError(string.Format(SR.DesignerActionPanel_CouldNotConvertValue, newValue, _propDesc.PropertyType)); + return; + } + else + { + newValue = PropertyDescriptor.Converter.ConvertFrom(_typeDescriptorContext, CultureInfo.CurrentCulture, newValue); + } + } + } + } + if (!object.Equals(_value, newValue)) + { + PropertyDescriptor.SetValue(_actionList, newValue); + // Update the value we're caching + _value = PropertyDescriptor.GetValue(_actionList); + OnValueChanged(); + } + } + catch (Exception e) + { + if (e is TargetInvocationException) + { + e = e.InnerException; + } + ActionPanel.ShowError(string.Format(SR.DesignerActionPanel_ErrorSettingValue, newValue, PropertyDescriptor.Name, e.Message)); + } + finally + { + _pushingValue = false; +#if MVWASSEMBLY + ActionPanel.InPushingValue = false; +#endif + } + } + + internal sealed override void UpdateActionItem(DesignerActionList actionList, DesignerActionItem actionItem, ToolTip toolTip, ref int currentTabIndex) + { + _actionList = actionList; + _propertyItem = (DesignerActionPropertyItem)actionItem; + _propDesc = null; + _typeDescriptorContext = null; + _value = PropertyDescriptor.GetValue(actionList); + OnPropertyTaskItemUpdated(toolTip, ref currentTabIndex); + _pushingValue = true; +#if MVWASSEMBLY + ActionPanel.InPushingValue = true; +#endif + try + { + OnValueChanged(); + } + finally + { + _pushingValue = false; +#if MVWASSEMBLY + ActionPanel.InPushingValue = false; +#endif + } + } + } + + private sealed class CheckBoxPropertyLine : PropertyLine + { + private CheckBox _checkBox; + + public CheckBoxPropertyLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel): base(serviceProvider, actionPanel) + { + } + + protected override void AddControls(List controls) + { + _checkBox = new CheckBox + { + BackColor = Color.Transparent, + CheckAlign = Drawing.ContentAlignment.MiddleLeft + }; + _checkBox.CheckedChanged += new EventHandler(OnCheckBoxCheckedChanged); + _checkBox.TextAlign = Drawing.ContentAlignment.MiddleLeft; + _checkBox.UseMnemonic = false; + _checkBox.ForeColor = ActionPanel.LabelForeColor; + controls.Add(_checkBox); + } + + public sealed override void Focus() => _checkBox.Focus(); + + public override Size LayoutControls(int top, int width, bool measureOnly) + { + Size checkBoxPreferredSize = _checkBox.GetPreferredSize(new Size(int.MaxValue, int.MaxValue)); + if (!measureOnly) + { + _checkBox.Location = new Point(LineLeftMargin, top + LineVerticalPadding / 2); + _checkBox.Size = checkBoxPreferredSize; + } + return checkBoxPreferredSize + new Size(LineLeftMargin + LineRightMargin, LineVerticalPadding); + } + + private void OnCheckBoxCheckedChanged(object sender, EventArgs e) + { + SetValue(_checkBox.Checked); + } + + protected override void OnPropertyTaskItemUpdated(ToolTip toolTip, ref int currentTabIndex) + { + _checkBox.Text = StripAmpersands(PropertyItem.DisplayName); + _checkBox.AccessibleDescription = PropertyItem.Description; + _checkBox.TabIndex = currentTabIndex++; + + toolTip.SetToolTip(_checkBox, PropertyItem.Description); + } + + protected override void OnValueChanged() + { + _checkBox.Checked = (bool)Value; + } + } + + private class TextBoxPropertyLine : PropertyLine + { + private TextBox _textBox; + private EditorLabel _readOnlyTextBoxLabel; + private Control _editControl; + private Label _label; + private int _editXPos; + private bool _textBoxDirty; + private Point _editRegionLocation; + private Point _editRegionRelativeLocation; + private Size _editRegionSize; + + public TextBoxPropertyLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) : base(serviceProvider, actionPanel) + { + } + + protected Control EditControl + { + get => _editControl; + } + + protected Point EditRegionLocation + { + get => _editRegionLocation; + } + + protected Point EditRegionRelativeLocation + { + get => _editRegionRelativeLocation; + } + + protected Size EditRegionSize + { + get => _editRegionSize; + } + + protected override void AddControls(List controls) + { + _label = new Label + { + BackColor = Color.Transparent, + ForeColor = ActionPanel.LabelForeColor, + TextAlign = Drawing.ContentAlignment.MiddleLeft, + UseMnemonic = false + }; + _readOnlyTextBoxLabel = new EditorLabel + { + BackColor = Color.Transparent, + ForeColor = SystemColors.WindowText, + TabStop = true, + TextAlign = Drawing.ContentAlignment.TopLeft, + UseMnemonic = false, + Visible = false + }; + _readOnlyTextBoxLabel.MouseClick += new MouseEventHandler(OnReadOnlyTextBoxLabelClick); + _readOnlyTextBoxLabel.Enter += new EventHandler(OnReadOnlyTextBoxLabelEnter); + _readOnlyTextBoxLabel.Leave += new EventHandler(OnReadOnlyTextBoxLabelLeave); + _readOnlyTextBoxLabel.KeyDown += new KeyEventHandler(OnReadOnlyTextBoxLabelKeyDown); + + _textBox = new TextBox + { + BorderStyle = BorderStyle.None, + TextAlign = System.Windows.Forms.HorizontalAlignment.Left, + Visible = false + }; + _textBox.TextChanged += new EventHandler(OnTextBoxTextChanged); + _textBox.KeyDown += new KeyEventHandler(OnTextBoxKeyDown); + _textBox.LostFocus += new EventHandler(OnTextBoxLostFocus); + + controls.Add(_readOnlyTextBoxLabel); + controls.Add(_textBox); + controls.Add(_label); + } + + public sealed override void Focus() + { + _editControl.Focus(); + } + + internal int GetEditRegionXPos() + { + if (string.IsNullOrEmpty(_label.Text)) + { + return LineLeftMargin; + } + return LineLeftMargin + _label.GetPreferredSize(new Size(int.MaxValue, int.MaxValue)).Width + TextBoxLineCenterMargin; + } + + protected virtual int GetTextBoxLeftPadding(int textBoxHeight) => TextBoxLineInnerPadding; + + protected virtual int GetTextBoxRightPadding(int textBoxHeight) => TextBoxLineInnerPadding; + + public override Size LayoutControls(int top, int width, bool measureOnly) + { + // Figure out our minimum width, Compare to proposed width, If we are smaller, widen the textbox to fit the line based on the bonus + int textBoxPreferredHeight = _textBox.GetPreferredSize(new Size(int.MaxValue, int.MaxValue)).Height; + textBoxPreferredHeight += TextBoxHeightFixup; + int height = textBoxPreferredHeight + LineVerticalPadding + TextBoxLineInnerPadding * 2 + 2; // 2 == border size + + int editRegionXPos = Math.Max(_editXPos, GetEditRegionXPos()); + int minimumWidth = editRegionXPos + EditInputWidth + LineRightMargin; + width = Math.Max(width, minimumWidth); + int textBoxWidthBonus = width - minimumWidth; + + if (!measureOnly) + { + _editRegionLocation = new Point(editRegionXPos, top + TextBoxTopPadding); + _editRegionRelativeLocation = new Point(editRegionXPos, TextBoxTopPadding); + _editRegionSize = new Size(EditInputWidth + textBoxWidthBonus, textBoxPreferredHeight + TextBoxLineInnerPadding * 2); + + _label.Location = new Point(LineLeftMargin, top); + int labelPreferredWidth = _label.GetPreferredSize(new Size(int.MaxValue, int.MaxValue)).Width; + _label.Size = new Size(labelPreferredWidth, height); + int specialPadding = 0; + if (_editControl is TextBox) + { + specialPadding = 2; + } + _editControl.Location = new Point(_editRegionLocation.X + GetTextBoxLeftPadding(textBoxPreferredHeight) + 1 + specialPadding, _editRegionLocation.Y + TextBoxLineInnerPadding + 1); + _editControl.Width = _editRegionSize.Width - GetTextBoxRightPadding(textBoxPreferredHeight) - GetTextBoxLeftPadding(textBoxPreferredHeight) - specialPadding; + _editControl.Height = _editRegionSize.Height - TextBoxLineInnerPadding * 2 - 1; + } + return new Size(width, height); + } + + protected virtual bool IsReadOnly() => IsReadOnlyProperty(PropertyDescriptor); + + protected override void OnPropertyTaskItemUpdated(ToolTip toolTip, ref int currentTabIndex) + { + _label.Text = StripAmpersands(PropertyItem.DisplayName); + _label.TabIndex = currentTabIndex++; + toolTip.SetToolTip(_label, PropertyItem.Description); + _textBoxDirty = false; + + if (IsReadOnly()) + { + _readOnlyTextBoxLabel.Visible = true; + _textBox.Visible = false; + // REVIEW: Setting Visible to false doesn't seem to work, so position far away + _textBox.Location = new Point(int.MaxValue, int.MaxValue); + _editControl = _readOnlyTextBoxLabel; + } + else + { + _readOnlyTextBoxLabel.Visible = false; + // REVIEW: Setting Visible to false doesn't seem to work, so position far away + _readOnlyTextBoxLabel.Location = new Point(int.MaxValue, int.MaxValue); + _textBox.Visible = true; + _editControl = _textBox; + } + _editControl.AccessibleDescription = PropertyItem.Description; + _editControl.AccessibleName = StripAmpersands(PropertyItem.DisplayName); + _editControl.TabIndex = currentTabIndex++; + _editControl.BringToFront(); + } + + protected virtual void OnReadOnlyTextBoxLabelClick(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + Focus(); + } + } + + private void OnReadOnlyTextBoxLabelEnter(object sender, EventArgs e) + { + _readOnlyTextBoxLabel.ForeColor = SystemColors.HighlightText; + _readOnlyTextBoxLabel.BackColor = SystemColors.Highlight; + } + + private void OnReadOnlyTextBoxLabelLeave(object sender, EventArgs e) + { + _readOnlyTextBoxLabel.ForeColor = SystemColors.WindowText; + _readOnlyTextBoxLabel.BackColor = SystemColors.Window; + } + + protected TypeConverter.StandardValuesCollection GetStandardValues() + { + TypeConverter converter = PropertyDescriptor.Converter; + if (converter != null && + converter.GetStandardValuesSupported(TypeDescriptorContext)) + { + return converter.GetStandardValues(TypeDescriptorContext); + } + return null; + } + + private void OnEditControlKeyDown(KeyEventArgs e) + { + if (e.KeyCode == Keys.Down) + { + e.Handled = true; + // Try to find the existing value and then pick the one after it + TypeConverter.StandardValuesCollection standardValues = GetStandardValues(); + if (standardValues != null) + { + for (int i = 0; i < standardValues.Count; i++) + { + if (object.Equals(Value, standardValues[i])) + { + if (i < standardValues.Count - 1) + { + SetValue(standardValues[i + 1]); + } + return; + } + } + // Previous value wasn't found, select the first one by default + if (standardValues.Count > 0) + { + SetValue(standardValues[0]); + } + } + return; + } + + if (e.KeyCode == Keys.Up) + { + e.Handled = true; + // Try to find the existing value and then pick the one before it + TypeConverter.StandardValuesCollection standardValues = GetStandardValues(); + if (standardValues != null) + { + for (int i = 0; i < standardValues.Count; i++) + { + if (object.Equals(Value, standardValues[i])) + { + if (i > 0) + { + SetValue(standardValues[i - 1]); + } + return; + } + } + // Previous value wasn't found, select the first one by default + if (standardValues.Count > 0) + { + SetValue(standardValues[standardValues.Count - 1]); + } + } + return; + } + } + + private void OnReadOnlyTextBoxLabelKeyDown(object sender, KeyEventArgs e) + { + // Delegate the rest of the processing to a common helper + OnEditControlKeyDown(e); + } + + private void OnTextBoxKeyDown(object sender, KeyEventArgs e) + { + if (ActionPanel.DropDownActive) + { + return; + } + + if (e.KeyCode == Keys.Enter) + { + UpdateValue(); + e.Handled = true; + return; + } + // Delegate the rest of the processing to a common helper + OnEditControlKeyDown(e); + } + + private void OnTextBoxLostFocus(object sender, EventArgs e) + { + if (ActionPanel.DropDownActive) + { + return; + } + UpdateValue(); + } + + private void OnTextBoxTextChanged(object sender, EventArgs e) => _textBoxDirty = true; + + protected override void OnValueChanged() => _editControl.Text = PropertyDescriptor.Converter.ConvertToString(TypeDescriptorContext, Value); + + public override void PaintLine(Graphics g, int lineWidth, int lineHeight) + { + Rectangle editRect = new Rectangle(EditRegionRelativeLocation, EditRegionSize); + g.FillRectangle(SystemBrushes.Window, editRect); + g.DrawRectangle(SystemPens.ControlDark, editRect); + } + + internal void SetEditRegionXPos(int xPos) + { + // Ignore the x-position if we have no text. This allows the textbox to span the entire width of the panel. + if (!string.IsNullOrEmpty(_label.Text)) + { + _editXPos = xPos; + } + else + { + _editXPos = LineLeftMargin; + } + } + + private void UpdateValue() + { + if (_textBoxDirty) + { + SetValue(_editControl.Text); + _textBoxDirty = false; + } + } + + /// + /// Custom label that provides accurate accessibility information and focus abilities. + /// + private sealed class EditorLabel : Label + { + public EditorLabel() + { + SetStyle(ControlStyles.Selectable, true); + } + + protected override AccessibleObject CreateAccessibilityInstance() => new EditorLabelAccessibleObject(this); + + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + // Since we are not a standard focusable control, we have to raise our own accessibility events. + // objectID = OBJID_WINDOW, childID = CHILDID_SELF - 1 (the -1 is because WinForms always adds 1 to the value) (these consts are defined in winuser.h) + AccessibilityNotifyClients(AccessibleEvents.Focus, 0, -1); + } + + protected override bool IsInputKey(Keys keyData) + { + if (keyData == Keys.Down || + keyData == Keys.Up) + { + return true; + } + return base.IsInputKey(keyData); + } + + private sealed class EditorLabelAccessibleObject : ControlAccessibleObject + { + public EditorLabelAccessibleObject(EditorLabel owner) : base(owner) + { + } + + public override string Value + { + get => Owner.Text; + } + } + } + } + + private sealed class EditorPropertyLine : TextBoxPropertyLine, IWindowsFormsEditorService, IServiceProvider + { + private EditorButton _button; + private UITypeEditor _editor; + private bool _hasSwatch; + private Image _swatch; + private FlyoutDialog _dropDownHolder; + private bool _ignoreNextSelectChange = false; + private bool _ignoreDropDownValue; + + public EditorPropertyLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) : base(serviceProvider, actionPanel) + { + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private void ActivateDropDown() + { + if (_editor != null) + { + try + { + object newValue = _editor.EditValue(TypeDescriptorContext, this, Value); + SetValue(newValue); + } + catch (Exception ex) + { + ActionPanel.ShowError(string.Format(SR.DesignerActionPanel_ErrorActivatingDropDown, ex.Message)); + } + } + else + { + ListBox listBox = new ListBox + { + BorderStyle = BorderStyle.None, + IntegralHeight = false, + Font = ActionPanel.Font + }; + listBox.SelectedIndexChanged += new EventHandler(OnListBoxSelectedIndexChanged); + listBox.KeyDown += new KeyEventHandler(OnListBoxKeyDown); + TypeConverter.StandardValuesCollection standardValues = GetStandardValues(); + if (standardValues != null) + { + foreach (object o in standardValues) + { + string newItem = PropertyDescriptor.Converter.ConvertToString(TypeDescriptorContext, CultureInfo.CurrentCulture, o); + listBox.Items.Add(newItem); + + if ((o != null) && o.Equals(Value)) + { + listBox.SelectedItem = newItem; + } + } + } + + // All measurement code borrowed from WinForms PropertyGridView.cs + int maxWidth = 0; + // The listbox draws with GDI, not GDI+. So, we use a normal DC here. + IntPtr hdc = UnsafeNativeMethods.GetDC(new HandleRef(listBox, listBox.Handle)); + IntPtr hFont = listBox.Font.ToHfont(); + NativeMethods.CommonHandles.GdiHandleCollector.Add(); + NativeMethods.TEXTMETRIC tm = new NativeMethods.TEXTMETRIC(); + try + { + hFont = SafeNativeMethods.SelectObject(new HandleRef(listBox, hdc), new HandleRef(listBox.Font, hFont)); + if (listBox.Items.Count > 0) + { + NativeMethods.SIZE textSize = new NativeMethods.SIZE(); + foreach (string s in listBox.Items) + { + SafeNativeMethods.GetTextExtentPoint32(new HandleRef(listBox, hdc), s, s.Length, textSize); + maxWidth = Math.Max((int)textSize.cx, maxWidth); + } + } + SafeNativeMethods.GetTextMetrics(new HandleRef(listBox, hdc), ref tm); + // border + padding + scrollbar + maxWidth += 2 + tm.tmMaxCharWidth + SystemInformation.VerticalScrollBarWidth; + hFont = SafeNativeMethods.SelectObject(new HandleRef(listBox, hdc), new HandleRef(listBox.Font, hFont)); + } + finally + { + SafeNativeMethods.DeleteObject(new HandleRef(listBox.Font, hFont)); + UnsafeNativeMethods.ReleaseDC(new HandleRef(listBox, listBox.Handle), new HandleRef(listBox, hdc)); + } + + listBox.Height = Math.Max(tm.tmHeight + 2, Math.Min(ListBoxMaximumHeight, listBox.PreferredHeight)); + listBox.Width = Math.Max(maxWidth, EditRegionSize.Width); + _ignoreDropDownValue = false; + try + { + ShowDropDown(listBox, SystemColors.ControlDark); + } + finally + { + listBox.SelectedIndexChanged -= new EventHandler(OnListBoxSelectedIndexChanged); + listBox.KeyDown -= new KeyEventHandler(OnListBoxKeyDown); + } + + if (!_ignoreDropDownValue) + { + if (listBox.SelectedItem != null) + { + SetValue(listBox.SelectedItem); + } + } + } + } + + protected override void AddControls(List controls) + { + base.AddControls(controls); + _button = new EditorButton(); + _button.Click += new EventHandler(OnButtonClick); + _button.GotFocus += new EventHandler(OnButtonGotFocus); + controls.Add(_button); + } + + private void CloseDropDown() + { + if (_dropDownHolder != null) + { + _dropDownHolder.Visible = false; + } + } + + protected override int GetTextBoxLeftPadding(int textBoxHeight) + { + if (_hasSwatch) + { + return base.GetTextBoxLeftPadding(textBoxHeight) + textBoxHeight + 2 * EditorLineSwatchPadding; + } + else + { + return base.GetTextBoxLeftPadding(textBoxHeight); + } + } + + protected override int GetTextBoxRightPadding(int textBoxHeight) => base.GetTextBoxRightPadding(textBoxHeight) + textBoxHeight + 2 * EditorLineButtonPadding; + + protected override bool IsReadOnly() + { + if (base.IsReadOnly()) + { + return true; + } + + // If we can't convert from string, we are readonly because we can't convert the user's input + bool converterReadOnly = !PropertyDescriptor.Converter.CanConvertFrom(TypeDescriptorContext, typeof(string)); + // If standard values are supported and are exclusive, we are readonly + bool standardValuesExclusive = + PropertyDescriptor.Converter.GetStandardValuesSupported(TypeDescriptorContext) && + PropertyDescriptor.Converter.GetStandardValuesExclusive(TypeDescriptorContext); + return converterReadOnly || standardValuesExclusive; + } + + public override Size LayoutControls(int top, int width, bool measureOnly) + { + Size size = base.LayoutControls(top, width, measureOnly); + if (!measureOnly) + { + int buttonHeight = EditRegionSize.Height - EditorLineButtonPadding * 2 - 1; + _button.Location = new Point(EditRegionLocation.X + EditRegionSize.Width - buttonHeight - EditorLineButtonPadding, EditRegionLocation.Y + EditorLineButtonPadding + 1); + _button.Size = new Size(buttonHeight, buttonHeight); + } + return size; + } + + private void OnButtonClick(object sender, EventArgs e) + { + ActivateDropDown(); + } + + private void OnButtonGotFocus(object sender, EventArgs e) + { + if (!_button.Ellipsis) + { + Focus(); + } + } + + private void OnListBoxKeyDown(object sender, KeyEventArgs e) + { + // Always respect the enter key and F4 + if (e.KeyData == Keys.Enter) + { + _ignoreNextSelectChange = false; + CloseDropDown(); + e.Handled = true; + } + else + { + // Ignore selected index change events when the user is navigating via the keyboard + _ignoreNextSelectChange = true; + } + } + + private void OnListBoxSelectedIndexChanged(object sender, EventArgs e) + { + // If we're ignoring this selected index change, do nothing + if (_ignoreNextSelectChange) + { + _ignoreNextSelectChange = false; + } + else + { + CloseDropDown(); + } + } + + protected override void OnPropertyTaskItemUpdated(ToolTip toolTip, ref int currentTabIndex) + { + _editor = (UITypeEditor)PropertyDescriptor.GetEditor(typeof(UITypeEditor)); + base.OnPropertyTaskItemUpdated(toolTip, ref currentTabIndex); + if (_editor != null) + { + _button.Ellipsis = (_editor.GetEditStyle(TypeDescriptorContext) == UITypeEditorEditStyle.Modal); + _hasSwatch = _editor.GetPaintValueSupported(TypeDescriptorContext); + } + else + { + _button.Ellipsis = false; + } + + if (_button.Ellipsis) + { + EditControl.AccessibleRole = (IsReadOnly() ? AccessibleRole.StaticText : AccessibleRole.Text); + } + else + { + EditControl.AccessibleRole = (IsReadOnly() ? AccessibleRole.DropList : AccessibleRole.ComboBox); + } + + _button.TabStop = _button.Ellipsis; + _button.TabIndex = currentTabIndex++; + _button.AccessibleRole = (_button.Ellipsis ? AccessibleRole.PushButton : AccessibleRole.ButtonDropDown); + _button.AccessibleDescription = EditControl.AccessibleDescription; + _button.AccessibleName = EditControl.AccessibleName; + } + + protected override void OnReadOnlyTextBoxLabelClick(object sender, MouseEventArgs e) + { + base.OnReadOnlyTextBoxLabelClick(sender, e); + if (e.Button == MouseButtons.Left) + { + if (ActionPanel.DropDownActive) + { + _ignoreDropDownValue = true; + CloseDropDown(); + } + else + { + ActivateDropDown(); + } + } + } + + protected override void OnValueChanged() + { + base.OnValueChanged(); + _swatch = null; + if (_hasSwatch) + { + ActionPanel.Invalidate(new Rectangle(EditRegionLocation, EditRegionSize), false); + } + } + + public override void PaintLine(Graphics g, int lineWidth, int lineHeight) + { + base.PaintLine(g, lineWidth, lineHeight); + if (_hasSwatch) + { + if (_swatch == null) + { + int width = EditRegionSize.Height - EditorLineSwatchPadding * 2; + int height = width - 1; + _swatch = new Bitmap(width, height); + Rectangle rect = new Rectangle(1, 1, width - 2, height - 2); + using (Graphics swatchGraphics = Graphics.FromImage(_swatch)) + { + _editor.PaintValue(Value, swatchGraphics, rect); + swatchGraphics.DrawRectangle(SystemPens.ControlDark, new Rectangle(0, 0, width - 1, height - 1)); + } + } + g.DrawImage(_swatch, new Point(EditRegionRelativeLocation.X + 2, EditorLineSwatchPadding + 5)); + } + } + + protected internal override bool ProcessDialogKey(Keys keyData) + { + // Do this here rather than in OnKeyDown because if hierarchy is properly set, VS is going to eat the F4 in PreProcessMessage, preventing it from ever getting to an OnKeyDown on this control. Doing it here also allow to not hook up to multiple events for each button. + if (!_button.Focused && !_button.Ellipsis) + { + if ((keyData == (Keys.Alt | Keys.Down)) || (keyData == (Keys.Alt | Keys.Up)) || (keyData == Keys.F4)) + { + if (!ActionPanel.DropDownActive) + { + ActivateDropDown(); + } + else + CloseDropDown(); + return true; + } + // Not passing Alt key event to base class to prevent closing 'Combobox Tasks window' + else if ((keyData & Keys.Alt) == Keys.Alt) + return true; + } + return base.ProcessDialogKey(keyData); + } + + private void ShowDropDown(Control hostedControl, Color borderColor) + { + hostedControl.Width = Math.Max(hostedControl.Width, EditRegionSize.Width - 2); + _dropDownHolder = new DropDownHolder(hostedControl, ActionPanel, borderColor, ActionPanel.Font, this); + if (ActionPanel.RightToLeft != RightToLeft.Yes) + { + Rectangle editorBounds = new Rectangle(Point.Empty, EditRegionSize); + Size dropDownSize = _dropDownHolder.Size; + Point editorLocation = ActionPanel.PointToScreen(EditRegionLocation); + Rectangle rectScreen = Screen.FromRectangle(ActionPanel.RectangleToScreen(editorBounds)).WorkingArea; + dropDownSize.Width = Math.Max(editorBounds.Width + 1, dropDownSize.Width); + editorLocation.X = Math.Min(rectScreen.Right - dropDownSize.Width, // min = right screen edge clip + Math.Max(rectScreen.X, editorLocation.X + editorBounds.Right - dropDownSize.Width)); // max = left screen edge clip + editorLocation.Y += editorBounds.Y; + if (rectScreen.Bottom < (dropDownSize.Height + editorLocation.Y + editorBounds.Height)) + { + editorLocation.Y -= dropDownSize.Height + 1; + } + else + { + editorLocation.Y += editorBounds.Height; + } + _dropDownHolder.Location = editorLocation; + } + else + { + _dropDownHolder.RightToLeft = ActionPanel.RightToLeft; + Rectangle editorBounds = new Rectangle(Point.Empty, EditRegionSize); + Size dropDownSize = _dropDownHolder.Size; + Point editorLocation = ActionPanel.PointToScreen(EditRegionLocation); + Rectangle rectScreen = Screen.FromRectangle(ActionPanel.RectangleToScreen(editorBounds)).WorkingArea; + dropDownSize.Width = Math.Max(editorBounds.Width + 1, dropDownSize.Width); + editorLocation.X = Math.Min(rectScreen.Right - dropDownSize.Width, // min = right screen edge clip + Math.Max(rectScreen.X, editorLocation.X - editorBounds.Width)); // max = left screen edge clip + editorLocation.Y += editorBounds.Y; + if (rectScreen.Bottom < (dropDownSize.Height + editorLocation.Y + editorBounds.Height)) + { + editorLocation.Y -= dropDownSize.Height + 1; + } + else + { + editorLocation.Y += editorBounds.Height; + } + _dropDownHolder.Location = editorLocation; + } + + ActionPanel.InMethodInvoke = true; + ActionPanel.SetDropDownActive(true); + try + { + _dropDownHolder.ShowDropDown(_button); + } + finally + { + _button.ResetMouseStates(); + ActionPanel.SetDropDownActive(false); + ActionPanel.InMethodInvoke = false; + } + } + + #region IWindowsFormsEditorService implementation + void IWindowsFormsEditorService.CloseDropDown() + { + CloseDropDown(); + } + + void IWindowsFormsEditorService.DropDownControl(Control control) + { + ShowDropDown(control, ActionPanel.BorderColor); + } + + DialogResult IWindowsFormsEditorService.ShowDialog(Form dialog) + { + IUIService uiService = (IUIService)ServiceProvider.GetService(typeof(IUIService)); + if (uiService != null) + { + return uiService.ShowDialog(dialog); + } + return dialog.ShowDialog(); + } + #endregion + + #region IServiceProvider implementation + object IServiceProvider.GetService(Type serviceType) + { + // Inject this class as the IWindowsFormsEditroService so drop-down custom editors can work + if (serviceType == typeof(IWindowsFormsEditorService)) + { + return this; + } + return ServiceProvider.GetService(serviceType); + } + #endregion + + private class DropDownHolder : FlyoutDialog + { + private readonly EditorPropertyLine _parent; + public DropDownHolder(Control hostedControl, Control parentControl, Color borderColor, Font font, EditorPropertyLine parent) : base(hostedControl, parentControl, borderColor, font) + { + _parent = parent; + _parent.ActionPanel.SetDropDownActive(true); + } + + protected override void OnClosed(EventArgs e) + { + base.OnClosed(e); + _parent.ActionPanel.SetDropDownActive(false); + } + + protected override bool ProcessDialogKey(Keys keyData) + { + if (keyData == Keys.Escape) + { + // Indicates that the selection was aborted so we should ignore the value + _parent._ignoreDropDownValue = true; + Visible = false; + return true; + } + + return base.ProcessDialogKey(keyData); + } + } + + internal class FlyoutDialog : Form + { + private readonly Control _hostedControl; + private readonly Control _parentControl; + + public FlyoutDialog(Control hostedControl, Control parentControl, Color borderColor, Font font) + { + _hostedControl = hostedControl; + _parentControl = parentControl; + BackColor = SystemColors.Window; + ControlBox = false; + Font = font; + FormBorderStyle = FormBorderStyle.None; + MinimizeBox = false; + MaximizeBox = false; + ShowInTaskbar = false; + StartPosition = FormStartPosition.Manual; + Text = string.Empty; + SuspendLayout(); + try + { + Controls.Add(hostedControl); + int width = Math.Max(_hostedControl.Width, SystemInformation.MinimumWindowSize.Width); + int height = Math.Max(_hostedControl.Height, SystemInformation.MinimizedWindowSize.Height); + if (!borderColor.IsEmpty) + { + DockPadding.All = 1; + BackColor = borderColor; + width += 2; + height += 4; + } + _hostedControl.Dock = DockStyle.Fill; + + Width = width; + Height = height; + } + finally + { + ResumeLayout(); + } + } + + protected override CreateParams CreateParams + { + get + { + CreateParams cp = base.CreateParams; + cp.ExStyle |= NativeMethods.WS_EX_TOOLWINDOW; + cp.Style |= NativeMethods.WS_POPUP | NativeMethods.WS_BORDER; + cp.ClassStyle |= NativeMethods.CS_SAVEBITS; + if (_parentControl != null) + { + if (!_parentControl.IsDisposed) + { + cp.Parent = _parentControl.Handle; + } + } + return cp; + } + } + + public virtual void FocusComponent() + { + if (_hostedControl != null && Visible) + { + _hostedControl.Focus(); + } + } + // Lifted directly from PropertyGridView.DropDownHolder. Less destructive than using ShowDialog(). + public void DoModalLoop() + { + while (Visible) + { +#if MVWASSEMBLY + System.Windows.Forms.Application.DoEvents(); +#else + Application.DoEvents(); +#endif + UnsafeNativeMethods.MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 250, NativeMethods.QS_ALLINPUT, NativeMethods.MWMO_INPUTAVAILABLE); + } + } + + /// + /// General purpose method, based on Control.Contains()... Determines whether a given window (specified using native window handle) is a descendant of this control. This catches both contained descendants and 'owned' windows such as modal dialogs. Using window handles rather than Control objects allows it to catch un-managed windows as well. + /// + private bool OwnsWindow(IntPtr hWnd) + { + while (hWnd != IntPtr.Zero) + { + hWnd = UnsafeNativeMethods.GetWindowLong(new HandleRef(null, hWnd), NativeMethods.GWL_HWNDPARENT); + if (hWnd == IntPtr.Zero) + { + return false; + } + if (hWnd == Handle) + { + return true; + } + } + return false; + } + + protected override bool ProcessDialogKey(Keys keyData) + { + if ((keyData == (Keys.Alt | Keys.Down)) || + (keyData == (Keys.Alt | Keys.Up)) || + (keyData == Keys.F4)) + { + // Any of these keys indicates the selection is accepted + Visible = false; + return true; + } + return base.ProcessDialogKey(keyData); + } + + public void ShowDropDown(Control parent) + { + try + { + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(parent, parent.Handle)); + // Lifted directly from Form.ShowDialog()... + IntPtr hWndCapture = UnsafeNativeMethods.GetCapture(); + if (hWndCapture != IntPtr.Zero) + { + UnsafeNativeMethods.SendMessage(new HandleRef(null, hWndCapture), NativeMethods.WM_CANCELMODE, 0, 0); + SafeNativeMethods.ReleaseCapture(); + } + Visible = true; // NOTE: Do this AFTER creating handle and setting parent + FocusComponent(); + DoModalLoop(); + } + finally + { + + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(null, IntPtr.Zero)); + // sometimes activation goes to LALA land - if our parent control is still around, remind it to take focus. + if (parent != null && parent.Visible) + { + parent.Focus(); + } + } + } + + protected override void WndProc(ref Message m) + { + if (m.Msg == NativeMethods.WM_ACTIVATE) + { + if (Visible && NativeMethods.Util.LOWORD(unchecked((int)(long)m.WParam)) == NativeMethods.WA_INACTIVE) + { + if (!OwnsWindow((IntPtr)m.LParam)) + { + Visible = false; + if (m.LParam == IntPtr.Zero) + { //we 're switching process, also dismiss the parent + Control toplevel = _parentControl.TopLevelControl; + if (toplevel is ToolStripDropDown dropDown) + { + // if it's a toolstrip dropdown let it know that we have a specific close reason. + dropDown.Close(); + } + else if (toplevel != null) + { + toplevel.Visible = false; + } + } + return; + } + } + } + base.WndProc(ref m); + } + } + + #region Interop definitions + private static class NativeMethods + { + public const int WM_ACTIVATE = 0x0006, + WM_CANCELMODE = 0x001F, + WM_MOUSEACTIVATE = 0x0021, + WM_NCLBUTTONDOWN = 0x00A1, + WM_NCRBUTTONDOWN = 0x00A4, + WM_NCMBUTTONDOWN = 0x00A7, + WM_LBUTTONDOWN = 0x0201, + WM_RBUTTONDOWN = 0x0204, + WM_MBUTTONDOWN = 0x0207, + WA_INACTIVE = 0, + WA_ACTIVE = 1, + WS_EX_TOOLWINDOW = 0x00000080, + WS_POPUP = unchecked((int)0x80000000), + WS_BORDER = 0x00800000, + GWL_HWNDPARENT = (-8), + QS_KEY = 0x0001, + QS_MOUSEMOVE = 0x0002, + QS_MOUSEBUTTON = 0x0004, + QS_POSTMESSAGE = 0x0008, + QS_TIMER = 0x0010, + QS_PAINT = 0x0020, + QS_SENDMESSAGE = 0x0040, + QS_HOTKEY = 0x0080, + QS_ALLPOSTMESSAGE = 0x0100, + QS_MOUSE = QS_MOUSEMOVE | QS_MOUSEBUTTON, + QS_INPUT = QS_MOUSE | QS_KEY, + QS_ALLEVENTS = QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY, + QS_ALLINPUT = QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE, + CS_SAVEBITS = 0x0800; + + public const int MWMO_INPUTAVAILABLE = 0x0004; // don't use MWMO_WAITALL, see ddb#176342 + + internal static class Util + { + public static int LOWORD(int n) => n & 0xffff; + } + + public static class CommonHandles + { + public static HandleCollector GdiHandleCollector = new HandleCollector("GDI", 500); + public static HandleCollector HdcHandleCollector = new HandleCollector("HDC", 2); + } + + [StructLayout(LayoutKind.Sequential)] + public class SIZE + { + public int cx = 0; + public int cy = 0; + public SIZE() + { + } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct TEXTMETRIC + { + public int tmHeight; + public int tmAscent; + public int tmDescent; + public int tmInternalLeading; + public int tmExternalLeading; + public int tmAveCharWidth; + public int tmMaxCharWidth; + public int tmWeight; + public int tmOverhang; + public int tmDigitizedAspectX; + public int tmDigitizedAspectY; + public char tmFirstChar; + public char tmLastChar; + public char tmDefaultChar; + public char tmBreakChar; + public byte tmItalic; + public byte tmUnderlined; + public byte tmStruckOut; + public byte tmPitchAndFamily; + public byte tmCharSet; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct TEXTMETRICA + { + public int tmHeight; + public int tmAscent; + public int tmDescent; + public int tmInternalLeading; + public int tmExternalLeading; + public int tmAveCharWidth; + public int tmMaxCharWidth; + public int tmWeight; + public int tmOverhang; + public int tmDigitizedAspectX; + public int tmDigitizedAspectY; + public byte tmFirstChar; + public byte tmLastChar; + public byte tmDefaultChar; + public byte tmBreakChar; + public byte tmItalic; + public byte tmUnderlined; + public byte tmStruckOut; + public byte tmPitchAndFamily; + public byte tmCharSet; + } + } + + private static class SafeNativeMethods + { + [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, EntryPoint = "DeleteObject", CharSet = System.Runtime.InteropServices.CharSet.Auto)] + private static extern bool IntDeleteObject(HandleRef hObject); + public static bool DeleteObject(HandleRef hObject) + { + NativeMethods.CommonHandles.GdiHandleCollector.Remove(); + return IntDeleteObject(hObject); + } + + [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] + public static extern bool ReleaseCapture(); + + [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + public static extern IntPtr SelectObject(HandleRef hDC, HandleRef hObject); + + [DllImport(ExternDllGdi32, SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + public static extern int GetTextExtentPoint32(HandleRef hDC, string str, int len, [In, Out] NativeMethods.SIZE size); + + [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + public static extern int GetTextMetricsW(HandleRef hDC, [In, Out] ref NativeMethods.TEXTMETRIC lptm); + + [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi)] + public static extern int GetTextMetricsA(HandleRef hDC, [In, Out] ref NativeMethods.TEXTMETRICA lptm); + + public static int GetTextMetrics(HandleRef hDC, ref NativeMethods.TEXTMETRIC lptm) + { + if (Marshal.SystemDefaultCharSize == 1) + { + // ANSI + NativeMethods.TEXTMETRICA lptmA = new NativeMethods.TEXTMETRICA(); + int retVal = SafeNativeMethods.GetTextMetricsA(hDC, ref lptmA); + lptm.tmHeight = lptmA.tmHeight; + lptm.tmAscent = lptmA.tmAscent; + lptm.tmDescent = lptmA.tmDescent; + lptm.tmInternalLeading = lptmA.tmInternalLeading; + lptm.tmExternalLeading = lptmA.tmExternalLeading; + lptm.tmAveCharWidth = lptmA.tmAveCharWidth; + lptm.tmMaxCharWidth = lptmA.tmMaxCharWidth; + lptm.tmWeight = lptmA.tmWeight; + lptm.tmOverhang = lptmA.tmOverhang; + lptm.tmDigitizedAspectX = lptmA.tmDigitizedAspectX; + lptm.tmDigitizedAspectY = lptmA.tmDigitizedAspectY; + lptm.tmFirstChar = (char)lptmA.tmFirstChar; + lptm.tmLastChar = (char)lptmA.tmLastChar; + lptm.tmDefaultChar = (char)lptmA.tmDefaultChar; + lptm.tmBreakChar = (char)lptmA.tmBreakChar; + lptm.tmItalic = lptmA.tmItalic; + lptm.tmUnderlined = lptmA.tmUnderlined; + lptm.tmStruckOut = lptmA.tmStruckOut; + lptm.tmPitchAndFamily = lptmA.tmPitchAndFamily; + lptm.tmCharSet = lptmA.tmCharSet; + return retVal; + } + else + { + // Unicode + return SafeNativeMethods.GetTextMetricsW(hDC, ref lptm); + } + } + } + + private static class UnsafeNativeMethods + { + [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + public static extern IntPtr GetWindowLong(HandleRef hWnd, int nIndex); + + [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + public static extern IntPtr SetWindowLong(HandleRef hWnd, int nIndex, HandleRef dwNewLong); + + [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] + public static extern int MsgWaitForMultipleObjectsEx(int nCount, IntPtr pHandles, int dwMilliseconds, int dwWakeMask, int dwFlags); + + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam); + + [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] + public static extern IntPtr GetCapture(); + + [DllImport(ExternDllUser32, ExactSpelling = true, EntryPoint = "GetDC", CharSet = CharSet.Auto)] + private static extern IntPtr IntGetDC(HandleRef hWnd); + public static IntPtr GetDC(HandleRef hWnd) + { + NativeMethods.CommonHandles.HdcHandleCollector.Add(); + return IntGetDC(hWnd); + } + + [DllImport(ExternDllUser32, ExactSpelling = true, EntryPoint = "ReleaseDC", CharSet = CharSet.Auto)] + private static extern int IntReleaseDC(HandleRef hWnd, HandleRef hDC); + public static int ReleaseDC(HandleRef hWnd, HandleRef hDC) + { + NativeMethods.CommonHandles.HdcHandleCollector.Remove(); + return IntReleaseDC(hWnd, hDC); + } + } + #endregion + + // Class that renders either the ellipsis or dropdown button + internal sealed class EditorButton : Button + { + private bool _mouseOver; + private bool _mouseDown; + private bool _ellipsis; + + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + + if (e.Button == MouseButtons.Left) + { + _mouseDown = true; + } + } + + protected override void OnMouseEnter(EventArgs e) + { + base.OnMouseEnter(e); + _mouseOver = true; + } + + protected override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + _mouseOver = false; + } + + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + + if (e.Button == MouseButtons.Left) + { + _mouseDown = false; + } + } + + public bool Ellipsis + { + get => _ellipsis; + set => _ellipsis = value; + } + + protected override void OnPaint(PaintEventArgs e) + { + Graphics g = e.Graphics; + if (_ellipsis) + { + PushButtonState buttonState = PushButtonState.Normal; + if (_mouseDown) + { + buttonState = PushButtonState.Pressed; + } + else if (_mouseOver) + { + buttonState = PushButtonState.Hot; + } + ButtonRenderer.DrawButton(g, new Rectangle(-1, -1, Width + 2, Height + 2), "…", Font, Focused, buttonState); + } + else + { + if (ComboBoxRenderer.IsSupported) + { + ComboBoxState state = ComboBoxState.Normal; + if (Enabled) + { + if (_mouseDown) + { + state = ComboBoxState.Pressed; + } + else if (_mouseOver) + { + state = ComboBoxState.Hot; + } + } + else + { + state = ComboBoxState.Disabled; + } + ComboBoxRenderer.DrawDropDownButton(g, new Rectangle(0, 0, Width, Height), state); + } + else + { + PushButtonState buttonState = PushButtonState.Normal; + if (Enabled) + { + if (_mouseDown) + { + buttonState = PushButtonState.Pressed; + } + else if (_mouseOver) + { + buttonState = PushButtonState.Hot; + } + } + else + { + buttonState = PushButtonState.Disabled; + } + + ButtonRenderer.DrawButton(g, new Rectangle(-1, -1, Width + 2, Height + 2), string.Empty, Font, Focused, buttonState); + // Draw the arrow icon + try + { + Icon icon = new Icon(typeof(DesignerActionPanel), "Arrow.ico"); + try + { + Bitmap arrowBitmap = icon.ToBitmap(); + // Make sure we draw properly under high contrast by re-mapping the arrow color to the WindowText color + ImageAttributes attrs = new ImageAttributes(); + try + { + ColorMap cm = new ColorMap + { + OldColor = Color.Black, + NewColor = SystemColors.WindowText + }; + attrs.SetRemapTable(new ColorMap[] { cm }, ColorAdjustType.Bitmap); + int imageWidth = arrowBitmap.Width; + int imageHeight = arrowBitmap.Height; + g.DrawImage(arrowBitmap, new Rectangle((Width - imageWidth + 1) / 2, (Height - imageHeight + 1) / 2, imageWidth, imageHeight), + 0, 0, imageWidth, imageWidth, GraphicsUnit.Pixel, attrs, null, IntPtr.Zero); + } + finally + { + if (attrs != null) + { + attrs.Dispose(); + } + } + } + finally + { + if (icon != null) + { + icon.Dispose(); + } + } + } + catch + { + } + } + if (Focused) + { + ControlPaint.DrawFocusRectangle(g, new Rectangle(2, 2, Width - 5, Height - 5)); + } + } + } + + public void ResetMouseStates() + { + _mouseDown = false; + _mouseOver = false; + Invalidate(); + } + } + } + + private class TextLine : Line + { + private Label _label; + private DesignerActionTextItem _textItem; + + public TextLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) : base(serviceProvider, actionPanel) + { + actionPanel.FontChanged += new EventHandler(OnParentControlFontChanged); + } + + public sealed override string FocusId + { + get => string.Empty; + } + + protected override void AddControls(List controls) + { + _label = new Label + { + BackColor = Color.Transparent, + ForeColor = ActionPanel.LabelForeColor, + TextAlign = Drawing.ContentAlignment.MiddleLeft, + UseMnemonic = false + }; + controls.Add(_label); + } + + public sealed override void Focus() + { + Debug.Fail("Should never try to focus a TextLine"); + } + + public override Size LayoutControls(int top, int width, bool measureOnly) + { + Size labelSize = _label.GetPreferredSize(new Size(int.MaxValue, int.MaxValue)); + if (!measureOnly) + { + _label.Location = new Point(LineLeftMargin, top + LineVerticalPadding / 2); + _label.Size = labelSize; + } + return labelSize + new Size(LineLeftMargin + LineRightMargin, LineVerticalPadding); + } + + private void OnParentControlFontChanged(object sender, EventArgs e) + { + if (_label != null && _label.Font != null) + { + _label.Font = GetFont(); + } + } + + protected virtual Font GetFont() + { + return ActionPanel.Font; + } + + internal override void UpdateActionItem(DesignerActionList actionList, DesignerActionItem actionItem, ToolTip toolTip, ref int currentTabIndex) + { + _textItem = (DesignerActionTextItem)actionItem; + _label.Text = StripAmpersands(_textItem.DisplayName); + _label.Font = GetFont(); + _label.TabIndex = currentTabIndex++; + toolTip.SetToolTip(_label, _textItem.Description); + } + } + + private sealed class HeaderLine : TextLine + { + public HeaderLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) : base(serviceProvider, actionPanel) + { + } + + protected override Font GetFont() => new Font(ActionPanel.Font, FontStyle.Bold); + } + + private sealed class SeparatorLine : Line + { + private readonly bool _isSubSeparator; + public SeparatorLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) : this(serviceProvider, actionPanel, false) + { + } + + public SeparatorLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel, bool isSubSeparator) : base(serviceProvider, actionPanel) + { + _isSubSeparator = isSubSeparator; + } + + public sealed override string FocusId + { + get => string.Empty; + } + + public bool IsSubSeparator => _isSubSeparator; + + protected override void AddControls(List controls) + { + } + + public sealed override void Focus() => Debug.Fail("Should never try to focus a SeparatorLine"); + + public override Size LayoutControls(int top, int width, bool measureOnly) => new Size(MinimumWidth, 1); + + public override void PaintLine(Graphics g, int lineWidth, int lineHeight) + { + using (Pen p = new Pen(ActionPanel.SeparatorColor)) + { + g.DrawLine(p, SeparatorHorizontalPadding, 0, lineWidth - (SeparatorHorizontalPadding + 1), 0); + } + } + + internal override void UpdateActionItem(DesignerActionList actionList, DesignerActionItem actionItem, ToolTip toolTip, ref int currentTabIndex) + { + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionTextItem.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionTextItem.cs new file mode 100644 index 00000000000..0f8c90cb7d7 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionTextItem.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.ComponentModel.Design +{ + public class DesignerActionTextItem : DesignerActionItem + { + public DesignerActionTextItem(string displayName, string category) : base(displayName, category, null) + { + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIService.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIService.cs new file mode 100644 index 00000000000..1d5eecdb41d --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIService.cs @@ -0,0 +1,113 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Forms.Design; + +namespace System.ComponentModel.Design +{ + public sealed class DesignerActionUIService : IDisposable + { + private DesignerActionUIStateChangeEventHandler _designerActionUIStateChangedEventHandler; + private readonly IServiceProvider _serviceProvider; //standard service provider + private readonly DesignerActionService _designerActionService; + + internal DesignerActionUIService(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + if (serviceProvider != null) + { + _serviceProvider = serviceProvider; + IDesignerHost host = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost)); + host.AddService(typeof(DesignerActionUIService), this); + _designerActionService = serviceProvider.GetService(typeof(DesignerActionService)) as DesignerActionService; + Debug.Assert(_designerActionService != null, "we should have created and registered the DAService first"); + } + } + + /// + /// Disposes all resources and unhooks all events. + /// + [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] + public void Dispose() + { + if (_serviceProvider != null) + { + IDesignerHost host = (IDesignerHost)_serviceProvider.GetService(typeof(IDesignerHost)); + if (host != null) + { + host.RemoveService(typeof(DesignerActionUIService)); + } + } + } + + /// + /// This event is thrown whenever a request is made to show/hide the ui + /// + public event DesignerActionUIStateChangeEventHandler DesignerActionUIStateChange + { + add => _designerActionUIStateChangedEventHandler += value; + remove => _designerActionUIStateChangedEventHandler -= value; + } + + public void HideUI(IComponent component) + { + OnDesignerActionUIStateChange(new DesignerActionUIStateChangeEventArgs(component, DesignerActionUIStateChangeType.Hide)); + } + + public void ShowUI(IComponent component) + { + OnDesignerActionUIStateChange(new DesignerActionUIStateChangeEventArgs(component, DesignerActionUIStateChangeType.Show)); + } + + /// + /// This is a new Helper Method that the service provides to refresh the DesignerActionGlyph as well as DesignerActionPanels. + /// + public void Refresh(IComponent component) + { + OnDesignerActionUIStateChange(new DesignerActionUIStateChangeEventArgs(component, DesignerActionUIStateChangeType.Refresh)); + } + + /// + /// This fires our DesignerActionsChanged event. + /// + private void OnDesignerActionUIStateChange(DesignerActionUIStateChangeEventArgs e) + { + _designerActionUIStateChangedEventHandler?.Invoke(this, e); + } + + + public bool ShouldAutoShow(IComponent component) + { + // Check the designer options... + if (_serviceProvider != null) + { + if (_serviceProvider.GetService(typeof(DesignerOptionService)) is DesignerOptionService opts) + { + PropertyDescriptor p = opts.Options.Properties["ObjectBoundSmartTagAutoShow"]; + if (p != null && p.PropertyType == typeof(bool) && !(bool)p.GetValue(null)) + { + return false; + } + } + } + if (_designerActionService != null) + { + DesignerActionListCollection coll = _designerActionService.GetComponentActions(component); + if (coll != null && coll.Count > 0) + { + for (int i = 0; i < coll.Count; i++) + { + if (coll[i].AutoShow) + { + return true; + } + } + } + } + return false; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventArgs.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventArgs.cs new file mode 100644 index 00000000000..47e19a70b04 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventArgs.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.ComponentModel.Design +{ + /// + /// This EventArgs class is used by the DesignerActionService to signify that there has been a change in DesignerActionLists (added or removed) on the related object. + /// + public class DesignerActionUIStateChangeEventArgs : EventArgs + { + private readonly object _relatedObject; + private readonly DesignerActionUIStateChangeType _changeType; //type of change + + /// + /// Constructor that requires the object in question, the type of change and the remaining actionlists left for the object. on the related object. + /// + public DesignerActionUIStateChangeEventArgs(object relatedObject, DesignerActionUIStateChangeType changeType) + { + _relatedObject = relatedObject; + _changeType = changeType; + } + + /// + /// The type of changed that caused the related event to be thrown. + /// + public DesignerActionUIStateChangeType ChangeType + { + get => _changeType; + } + + /// + /// The object this change is related to. + /// + public object RelatedObject + { + get => _relatedObject; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventHandler.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventHandler.cs new file mode 100644 index 00000000000..48b12fe08b6 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventHandler.cs @@ -0,0 +1,8 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.ComponentModel.Design +{ + public delegate void DesignerActionUIStateChangeEventHandler(object sender, DesignerActionUIStateChangeEventArgs e); +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeType.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeType.cs new file mode 100644 index 00000000000..e4f35eed025 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeType.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.ComponentModel.Design +{ + public enum DesignerActionUIStateChangeType + { + Show, + Hide, + Refresh + } +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/SelectionService.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/SelectionService.cs index 7860cbc1f09..9a4e501cb00 100644 --- a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/SelectionService.cs +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/SelectionService.cs @@ -166,6 +166,11 @@ private void OnTransactionOpened(object sender, EventArgs e) _state[s_stateTransaction] = true; } + internal object PrimarySelection + { + get => (_selection != null && _selection.Count > 0) ? _selection[0] : null; + } + /// /// Removes the given selection from the selection list. /// diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/VsPropertyGrid.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/VsPropertyGrid.cs new file mode 100644 index 00000000000..85c09db2fb5 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/VsPropertyGrid.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; +using System.IO; +using System.Windows.Forms; + +namespace System.ComponentModel.Design +{ + internal class VsPropertyGrid : PropertyGrid + { + private static readonly Size s_iCON_SIZE = new Size(16, 16); + private static Size s_iconSize = s_iCON_SIZE; + private static bool s_isScalingInitialized = false; + + public VsPropertyGrid(IServiceProvider serviceProvider) : base() + { + } + + protected override Bitmap SortByPropertyImage + { + get => GetBitmap("PBAlpha"); + } + + protected override Bitmap SortByCategoryImage + { + get => GetBitmap("PBCatego", true); + } + + protected override Bitmap ShowPropertyPageImage + { + get => GetBitmap("PBPPage"); + } + + // try to find the best possible image + private Bitmap GetBitmap(string resourceName, bool setMagentaTransparent = false) + { + // this resource might be present in System.Windows.Forms.VisualStudio.15.0.dll if this code is running on dev14 or newer + Stream stream = BitmapSelector.GetResourceStream(typeof(PropertyGrid), resourceName + ".ico"); + Bitmap bitmap; + if (stream != null) + { + if (!VsPropertyGrid.s_isScalingInitialized) + { + if (DpiHelper.IsScalingRequired) + { + VsPropertyGrid.s_iconSize = DpiHelper.LogicalToDeviceUnits(s_iCON_SIZE); + } + VsPropertyGrid.s_isScalingInitialized = true; + } + // retrieve icon closest to the desired size + Icon icon = new Icon(stream, VsPropertyGrid.s_iconSize); + bitmap = icon.ToBitmap(); + icon.Dispose(); + } + else + { + // this resource must be present in System.Windows.Forms.dll if it is not available in System.Windows.Forms.VisualStudio.15.0.dll + bitmap = new Bitmap(BitmapSelector.GetResourceStream(typeof(PropertyGrid), resourceName + ".bmp")); + if (setMagentaTransparent) + { + bitmap.MakeTransparent(Color.Magenta); + } + } + return bitmap; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/AdornmentType.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/AdornmentType.cs new file mode 100644 index 00000000000..bbcf7cebd91 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/AdornmentType.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Design +{ + /// + /// Specifies numeric IDs for different types of adornments on a component. + /// + internal enum AdornmentType + { + /// + /// Specifies the type as grab handle adornments. + /// + GrabHandle = 1, + /// + /// Specifies the type as container selector adornments. + /// + ContainerSelector = 2, + /// + /// Specifies the type as the maximum size of any adornment. + /// + Maximum = 3, + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/BaseContextMenuStrip.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/BaseContextMenuStrip.cs new file mode 100644 index 00000000000..a424dc3444b --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/BaseContextMenuStrip.cs @@ -0,0 +1,318 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Design; + +namespace System.Windows.Forms.Design +{ + /// + /// This class is going to replace the shell contextMenu and uses the ContextMenuStrip. The ContextMenuStrip contains groups and groupOrder which it uses to add items to itself. ControlDesigners can add custom items to the contextMenu, using the new member to the group and add the groupOrder to the ContextMenu. + /// + internal class BaseContextMenuStrip : GroupedContextMenuStrip + { + private IServiceProvider serviceProvider; + private Component component; + private ToolStripMenuItem selectionMenuItem; + + public BaseContextMenuStrip(IServiceProvider provider, Component component) : base() + { + serviceProvider = provider; + this.component = component; + // Now initialiaze the contextMenu + InitializeContextMenu(); + } + + /// + /// Helper function to add the "View Code" menuItem. + /// + private void AddCodeMenuItem() + { + StandardCommandToolStripMenuItem codeMenuItem = new StandardCommandToolStripMenuItem(StandardCommands.ViewCode, SR.ContextMenuViewCode, "viewcode", serviceProvider); + Groups[StandardGroups.Code].Items.Add(codeMenuItem); + } + + /// + /// Helper function to add the "SendToBack/BringToFront" menuItem. + /// + private void AddZorderMenuItem() + { + StandardCommandToolStripMenuItem ZOrderMenuItem = new StandardCommandToolStripMenuItem(MenuCommands.BringToFront, SR.ContextMenuBringToFront, "bringToFront", serviceProvider); + Groups[StandardGroups.ZORder].Items.Add(ZOrderMenuItem); + ZOrderMenuItem = new StandardCommandToolStripMenuItem(MenuCommands.SendToBack, SR.ContextMenuSendToBack, "sendToBack", serviceProvider); + Groups[StandardGroups.ZORder].Items.Add(ZOrderMenuItem); + } + + /// + /// Helper function to add the "Alignment" menuItem. + /// + private void AddGridMenuItem() + { + StandardCommandToolStripMenuItem gridMenuItem = new StandardCommandToolStripMenuItem(MenuCommands.AlignToGrid, SR.ContextMenuAlignToGrid, "alignToGrid", serviceProvider); + Groups[StandardGroups.Grid].Items.Add(gridMenuItem); + } + + /// + /// Helper function to add the "Locked" menuItem. + /// + private void AddLockMenuItem() + { + StandardCommandToolStripMenuItem lockMenuItem = new StandardCommandToolStripMenuItem(MenuCommands.LockControls, SR.ContextMenuLockControls, "lockControls", serviceProvider); + Groups[StandardGroups.Lock].Items.Add(lockMenuItem); + } + + /// + /// Helper function to add the Select Parent menuItem. + /// + private void RefreshSelectionMenuItem() + { + int index = -1; + if (selectionMenuItem != null) + { + index = Items.IndexOf(selectionMenuItem); + Groups[StandardGroups.Selection].Items.Remove(selectionMenuItem); + Items.Remove(selectionMenuItem); + } + ArrayList parentControls = new ArrayList(); + int nParentControls = 0; + + // Get the currently selected Control + if (serviceProvider.GetService(typeof(ISelectionService)) is ISelectionService selectionService && serviceProvider.GetService(typeof(IDesignerHost)) is IDesignerHost host) + { + IComponent root = host.RootComponent; + Debug.Assert(root != null, "Null root component. Will be unable to build selection menu"); + if (selectionService.PrimarySelection is Control selectedControl && root != null && selectedControl != root) + { + Control parentControl = selectedControl.Parent; + while (parentControl != null) + { + if (parentControl.Site != null) + { + parentControls.Add(parentControl); + nParentControls++; + } + if (parentControl == root) + { + break; + } + parentControl = parentControl.Parent; + } + } + else if (selectionService.PrimarySelection is ToolStripItem) + { + ToolStripItem selectedItem = selectionService.PrimarySelection as ToolStripItem; + if (host.GetDesigner(selectedItem) is ToolStripItemDesigner itemDesigner) + { + parentControls = itemDesigner.AddParentTree(); + nParentControls = parentControls.Count; + } + } + } + if (nParentControls > 0) + { + selectionMenuItem = new ToolStripMenuItem(); + + IUIService uis = serviceProvider.GetService(typeof(IUIService)) as IUIService; + if (uis != null) + { + selectionMenuItem.DropDown.Renderer = (ToolStripProfessionalRenderer)uis.Styles["VsRenderer"]; + //Set the right Font + selectionMenuItem.DropDown.Font = (Font)uis.Styles["DialogFont"]; + if (uis.Styles["VsColorPanelText"] is Color) + { + selectionMenuItem.DropDown.ForeColor = (Color)uis.Styles["VsColorPanelText"]; + } + + } + + selectionMenuItem.Text = SR.ContextMenuSelect; + foreach (Component parent in parentControls) + { + ToolStripMenuItem selectListItem = new SelectToolStripMenuItem(parent, serviceProvider); + selectionMenuItem.DropDownItems.Add(selectListItem); + } + Groups[StandardGroups.Selection].Items.Add(selectionMenuItem); + // Re add the newly refreshed item.. + if (index != -1) + { + Items.Insert(index, selectionMenuItem); + } + } + } + + /// + /// Helper function to add the Verbs. + /// + private void AddVerbMenuItem() + { + //Add Designer Verbs.. + IMenuCommandService menuCommandService = (IMenuCommandService)serviceProvider.GetService(typeof(IMenuCommandService)); + if (menuCommandService != null) + { + DesignerVerbCollection verbCollection = menuCommandService.Verbs; + foreach (DesignerVerb verb in verbCollection) + { + DesignerVerbToolStripMenuItem verbItem = new DesignerVerbToolStripMenuItem(verb); + Groups[StandardGroups.Verbs].Items.Add(verbItem); + } + } + } + + /// + /// Helper function to add the "Cut/Copy/Paste/Delete" menuItem. + /// + private void AddEditMenuItem() + { + StandardCommandToolStripMenuItem stdMenuItem = new StandardCommandToolStripMenuItem(StandardCommands.Cut, SR.ContextMenuCut, "cut", serviceProvider); + Groups[StandardGroups.Edit].Items.Add(stdMenuItem); + stdMenuItem = new StandardCommandToolStripMenuItem(StandardCommands.Copy, SR.ContextMenuCopy, "copy", serviceProvider); + Groups[StandardGroups.Edit].Items.Add(stdMenuItem); + stdMenuItem = new StandardCommandToolStripMenuItem(StandardCommands.Paste, SR.ContextMenuPaste, "paste", serviceProvider); + Groups[StandardGroups.Edit].Items.Add(stdMenuItem); + stdMenuItem = new StandardCommandToolStripMenuItem(StandardCommands.Delete, SR.ContextMenuDelete, "delete", serviceProvider); + Groups[StandardGroups.Edit].Items.Add(stdMenuItem); + } + + /// + /// Helper function to add the "Properties" menuItem. + /// + private void AddPropertiesMenuItem() + { + StandardCommandToolStripMenuItem stdMenuItem = new StandardCommandToolStripMenuItem(StandardCommands.DocumentOutline, SR.ContextMenuDocumentOutline, "", serviceProvider); + Groups[StandardGroups.Properties].Items.Add(stdMenuItem); + stdMenuItem = new StandardCommandToolStripMenuItem(MenuCommands.DesignerProperties, SR.ContextMenuProperties, "properties", serviceProvider); + Groups[StandardGroups.Properties].Items.Add(stdMenuItem); + } + + /// + /// Basic Initialize method. + /// + private void InitializeContextMenu() + { + //this.Opening += new CancelEventHandler(OnContextMenuOpening); + Name = "designerContextMenuStrip"; + if (serviceProvider.GetService(typeof(IUIService)) is IUIService uis) + { + Renderer = (ToolStripProfessionalRenderer)uis.Styles["VsRenderer"]; + if (uis.Styles["VsColorPanelText"] is Color) + { + ForeColor = (Color)uis.Styles["VsColorPanelText"]; + } + } + GroupOrdering.AddRange(new string[] { StandardGroups.Code, StandardGroups.ZORder, StandardGroups.Grid, StandardGroups.Lock, StandardGroups.Verbs, StandardGroups.Custom, StandardGroups.Selection, StandardGroups.Edit, StandardGroups.Properties}); + // ADD MENUITEMS + AddCodeMenuItem(); + AddZorderMenuItem(); + AddGridMenuItem(); + AddLockMenuItem(); + AddVerbMenuItem(); + RefreshSelectionMenuItem(); + AddEditMenuItem(); + AddPropertiesMenuItem(); + } + + /// + /// Public function that allows the individual MenuItems to get refreshed each time the ContextMenu is opened. + /// + public override void RefreshItems() + { + if (serviceProvider.GetService(typeof(IUIService)) is IUIService uis) + { + Font = (Font)uis.Styles["DialogFont"]; + } + + foreach (ToolStripItem item in this.Items) + { + StandardCommandToolStripMenuItem stdItem = item as StandardCommandToolStripMenuItem; + if (stdItem != null) + { + stdItem.RefreshItem(); + } + } + RefreshSelectionMenuItem(); + } + + /// + /// A ToolStripMenuItem that gets added for the "Select" menuitem. + /// + private class SelectToolStripMenuItem : ToolStripMenuItem + { + private readonly Component _comp; + private readonly IServiceProvider _serviceProvider; + private readonly Type _itemType; + private bool _cachedImage = false; + private Image _image = null; + private static readonly string s_systemWindowsFormsNamespace = typeof(System.Windows.Forms.ToolStripItem).Namespace; + + public SelectToolStripMenuItem(Component c, IServiceProvider provider) + { + _comp = c; + _serviceProvider = provider; + // Get NestedSiteName... + string compName = null; + if (_comp != null) + { + ISite site = _comp.Site; + if (site != null) + { + if (site is INestedSite nestedSite && !string.IsNullOrEmpty(nestedSite.FullName)) + { + compName = nestedSite.FullName; + } + else if (!string.IsNullOrEmpty(site.Name)) + { + compName = site.Name; + } + } + } + Text = string.Format(SR.ToolStripSelectMenuItem, compName); + _itemType = c.GetType(); + } + + public override Image Image + { + get + { + // Defer loading the image until we're sure we need it + if (!_cachedImage) + { + _cachedImage = true; + // else attempt to get the resource from a known place in the manifest. if and only if the namespace of the type is System.Windows.Forms. else attempt to get the resource from a known place in the manifest + if (_itemType.Namespace == s_systemWindowsFormsNamespace) + { + _image = ToolboxBitmapAttribute.GetImageFromResource(_itemType, null, false); + } + + // if all else fails, throw up a default image. + if (_image == null) + { + _image = ToolboxBitmapAttribute.GetImageFromResource(_comp.GetType(), null, false); + } + } + return _image; + } + set + { + _image = value; + _cachedImage = true; + } + } + + /// + /// Items OnClick event, to select the Parent Control. + /// + protected override void OnClick(System.EventArgs e) + { + if (_serviceProvider.GetService(typeof(ISelectionService)) is ISelectionService selectionService) + { + selectionService.SetSelectedComponents(new object[] { _comp }, SelectionTypes.Replace); + } + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/Adorner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/Adorner.cs index 5f8bba74855..87d597cca99 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/Adorner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/Adorner.cs @@ -14,12 +14,17 @@ namespace System.Windows.Forms.Design.Behavior /// public sealed class Adorner { + private BehaviorService _behaviorService; //ptr back to the BehaviorService + private readonly GlyphCollection _glyphs; //collection of Glyphs that this particular Adorner manages + private bool _enabled; //enabled value - determines if Adorner gets paints & hits + /// /// Standard constructor. Creates a new GlyphCollection and by default is enabled. /// public Adorner() { - throw new NotImplementedException(SR.NotImplementedByDesign); + _glyphs = new GlyphCollection(); + _enabled = true; } /// @@ -28,8 +33,8 @@ public Adorner() /// public BehaviorService BehaviorService { - get => throw new NotImplementedException(SR.NotImplementedByDesign); - set => throw new NotImplementedException(SR.NotImplementedByDesign); + get => _behaviorService; + set => _behaviorService = value; } /// @@ -38,14 +43,29 @@ public BehaviorService BehaviorService /// public bool Enabled { - get => throw new NotImplementedException(SR.NotImplementedByDesign); - set => throw new NotImplementedException(SR.NotImplementedByDesign); + get => EnabledInternal; + set + { + if (value != EnabledInternal) + { + EnabledInternal = value; + Invalidate(); + } + } + } + internal bool EnabledInternal + { + get => _enabled; + set => _enabled = value; } /// /// Returns the stronly-typed Glyph collection. /// - public GlyphCollection Glyphs => throw new NotImplementedException(SR.NotImplementedByDesign); + public GlyphCollection Glyphs + { + get => _glyphs; + } /// /// /// @@ -53,7 +73,10 @@ public bool Enabled /// public void Invalidate() { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (_behaviorService != null) + { + _behaviorService.Invalidate(); + } } /// @@ -61,7 +84,10 @@ public void Invalidate() /// public void Invalidate(Rectangle rectangle) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (_behaviorService != null) + { + _behaviorService.Invalidate(rectangle); + } } /// @@ -69,7 +95,10 @@ public void Invalidate(Rectangle rectangle) /// public void Invalidate(Region region) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (_behaviorService != null) + { + _behaviorService.Invalidate(region); + } } } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs index 9501ebbf41d..0ae6485f571 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs @@ -135,6 +135,11 @@ internal Control AdornerWindowControl get => _adornerWindow; } + internal int AdornerWindowIndex + { + get => _adornerWindowIndex; + } + internal bool HasCapture { get => _captureBehavior != null; @@ -188,6 +193,11 @@ public Behavior CurrentBehavior } } + internal bool Dragging + { + get => _dragging; + } + internal bool CancelDrag { get => _cancelDrag; @@ -534,6 +544,12 @@ public Behavior PopBehavior(Behavior behavior) return behavior; } + internal void ProcessPaintMessage(Rectangle paintRect) + { + //Note, we don't call BehSvc.Invalidate because this will just cause the messages to recurse. Instead, invalidating this adornerWindow will just cause a "propagatePaint" and draw the glyphs. + _adornerWindow.Invalidate(paintRect); + } + /// /// Pushes a Behavior object onto the BehaviorStack. This is often done through hit-tested /// Glyph. @@ -1117,7 +1133,7 @@ private void HookMouse() if (_thisProcessID == 0) { AdornerWindow adornerWindow = AdornerWindow.s_adornerWindowList[0]; - UnsafeNativeMethods.GetWindowThreadProcessId(new HandleRef(adornerWindow, adornerWindow.Handle), out _thisProcessID); + SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(adornerWindow, adornerWindow.Handle), out _thisProcessID); } NativeMethods.HookProc hook = new NativeMethods.HookProc(MouseHookProc); @@ -1216,7 +1232,7 @@ private bool ProcessMouseMessage(IntPtr hWnd, int msg, int x, int y) { Debug.Assert(_thisProcessID != 0, "Didn't get our process id!"); // make sure the window is in our process - UnsafeNativeMethods.GetWindowThreadProcessId(new HandleRef(null, hWnd), out int pid); + SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(null, hWnd), out int pid); // if this isn't our process, bail if (pid != _thisProcessID) { @@ -1233,8 +1249,7 @@ private bool ProcessMouseMessage(IntPtr hWnd, int msg, int x, int y) }; NativeMethods.MapWindowPoints(IntPtr.Zero, adornerWindow.Handle, pt, 1); Message m = Message.Create(hWnd, msg, (IntPtr)0, (IntPtr)MAKELONG(pt.y, pt.x)); - - // DevDiv Bugs 79616, No one knows why we get an extra click here from VS. As a workaround, we check the TimeStamp and discard it. + // No one knows why we get an extra click here from VS. As a workaround, we check the TimeStamp and discard it. if (m.Msg == NativeMethods.WM_LBUTTONDOWN) { _lastLButtonDownTimeStamp = UnsafeNativeMethods.GetMessageTime(); @@ -1391,6 +1406,11 @@ DesignerVerbCollection IMenuCommandService.Verbs } } + internal void StartDragNotification() + { + _adornerWindow.StartDragNotification(); + } + private MenuCommand FindCommand(CommandID commandID, IMenuCommandService menuService) { Behavior behavior = GetAppropriateBehavior(_hitTestedGlyph); diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionBehavior.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionBehavior.cs new file mode 100644 index 00000000000..5fd4a883d5a --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionBehavior.cs @@ -0,0 +1,154 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// This is the Behavior that represents DesignerActions for a particular control. The DesignerActionBehavior is responsible for responding to the MouseDown message and either 1) selecting the control and changing the DesignerActionGlyph's image or 2) building up a chrome menu and requesting it to be shown. Also, this Behavior acts as a proxy between "clicked" context menu items and the actual DesignerActions that they represent. + /// + internal sealed class DesignerActionBehavior : Behavior + { + private readonly IComponent _relatedComponent;//The component we are bound to + private DesignerActionUI _parentUI;//ptr to the parenting UI, used for showing menus and setting selection + private DesignerActionListCollection _actionLists;//all the shortcuts! + private readonly IServiceProvider _serviceProvider; // we need to cache the service provider here to be able to create the panel with the proper arguments + private bool _ignoreNextMouseUp = false; + + /// + /// Constructor that calls base and caches off the action lists. + /// + internal DesignerActionBehavior(IServiceProvider serviceProvider, IComponent relatedComponent, DesignerActionListCollection actionLists, DesignerActionUI parentUI) + { + _actionLists = actionLists; + _serviceProvider = serviceProvider; + _relatedComponent = relatedComponent; + _parentUI = parentUI; + } + + /// + /// Returns the collection of DesignerActionLists this Behavior is managing. These will be dynamically updated (some can be removed, new ones can be added, etc...). + /// + internal DesignerActionListCollection ActionLists + { + get => _actionLists; + set => _actionLists = value; + } + + /// + /// Returns the parenting UI (a DesignerActionUI) + /// + internal DesignerActionUI ParentUI + { + get => _parentUI; + } + + /// + /// Returns the Component that this glyph is attached to. + /// + internal IComponent RelatedComponent + { + get => _relatedComponent; + } + + /// + /// Hides the designer action panel UI. + /// + internal void HideUI() + { + ParentUI.HideDesignerActionPanel(); + } + + internal DesignerActionPanel CreateDesignerActionPanel(IComponent relatedComponent) + { + // BUILD AND SHOW THE CHROME UI + DesignerActionListCollection lists = new DesignerActionListCollection(); + lists.AddRange(ActionLists); + DesignerActionPanel dap = new DesignerActionPanel(_serviceProvider); + dap.UpdateTasks(lists, new DesignerActionListCollection(), string.Format(SR.DesignerActionPanel_DefaultPanelTitle, relatedComponent.GetType().Name), null); + return dap; + } + + /// + /// Shows the designer action panel UI associated with this glyph. + /// + internal void ShowUI(Glyph g) + { + if (!(g is DesignerActionGlyph glyph)) + { + Debug.Fail("Why are we trying to 'showui' on a glyph that's not a DesignerActionGlyph?"); + return; + } + DesignerActionPanel dap = CreateDesignerActionPanel(RelatedComponent); + ParentUI.ShowDesignerActionPanel(RelatedComponent, dap, glyph); + } + + internal bool IgnoreNextMouseUp + { + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + set + { + _ignoreNextMouseUp = value; + } + } + + public override bool OnMouseDoubleClick(Glyph g, MouseButtons button, Point mouseLoc) + { + _ignoreNextMouseUp = true; + return true; + } + + public override bool OnMouseDown(Glyph g, MouseButtons button, Point mouseLoc) + { // we take the msg + return (!ParentUI.IsDesignerActionPanelVisible); + } + + /// + /// In response to a MouseUp, we will either 1) select the Glyph and control if not selected, or 2) Build up our context menu representing our DesignerActions and show it. + /// + public override bool OnMouseUp(Glyph g, MouseButtons button) + { + if (button != MouseButtons.Left || ParentUI == null) + { + return true; + } + bool returnValue = true; + if (ParentUI.IsDesignerActionPanelVisible) + { + HideUI(); + } + else if (!_ignoreNextMouseUp) + { + if (_serviceProvider != null) + { + ISelectionService selectionService = (ISelectionService)_serviceProvider.GetService(typeof(ISelectionService)); + if (selectionService != null) + { + if (selectionService.PrimarySelection != RelatedComponent) + { + List componentList = new List + { + RelatedComponent + }; + selectionService.SetSelectedComponents(componentList, SelectionTypes.Primary); + } + } + } + ShowUI(g); + } + else + { + returnValue = false; + } + _ignoreNextMouseUp = false; + return returnValue; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionGlyph.cs new file mode 100644 index 00000000000..a5f72397385 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionGlyph.cs @@ -0,0 +1,234 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// This Glyph represents the UI appended to a control when DesignerActions are available. Each image that represents these states are demand created. This is done because it is entirely possible that a DesignerActionGlyph will only ever be in one of these states during its lifetime... kind of sad really. + /// + internal sealed class DesignerActionGlyph : Glyph + { + internal const int CONTROLOVERLAP_X = 5; // number of pixels the anchor should be offset to the left of the control's upper-right + internal const int CONTROLOVERLAP_Y = 2; // number of pixels the anchor overlaps the control in the y-direction + + private Rectangle _bounds; // the bounds of our glyph + private readonly Adorner _adorner; // A ptr back to our adorner - so when we decide to change state, we can invalidate + private bool _mouseOver; // on mouse over, we shade our image differently, this is used to track that state + private Rectangle _alternativeBounds = Rectangle.Empty; // if !empty, this represents the bounds of the tray control this gyph is related to + private readonly Control _alternativeParent; // if this is valid - then the glyph will invalidate itself here instead of on the adorner + private bool _insidePaint; + private DockStyle _dockStyle; + private Bitmap _glyphImageClosed; + private Bitmap _glyphImageOpened; + + /// + /// Constructor that passes empty alternative bounds and parents. Typically this is done for control on the designer's surface since component tray glyphs will have these alternative values. + /// + public DesignerActionGlyph(DesignerActionBehavior behavior, Adorner adorner) : this(behavior, adorner, Rectangle.Empty, null) + { } + public DesignerActionGlyph(DesignerActionBehavior behavior, Rectangle alternativeBounds, Control alternativeParent) : this(behavior, null, alternativeBounds, alternativeParent) + { } + + /// + /// Constructor that sets the dropdownbox size, creates a our hottrack brush and invalidates the glyph (to configure location). + /// + private DesignerActionGlyph(DesignerActionBehavior behavior, Adorner adorner, Rectangle alternativeBounds, Control alternativeParent) : base(behavior) + { + _adorner = adorner; + _alternativeBounds = alternativeBounds; + _alternativeParent = alternativeParent; + Invalidate(); + } + + /// + /// Returns the bounds of our glyph. This is used by the related Behavior to determine where to show the contextmenu (list of actions). + /// + public override Rectangle Bounds + { + get => _bounds; + } + + + public DockStyle DockEdge + { + get => _dockStyle; + set + { + if (_dockStyle != value) + { + _dockStyle = value; + } + } + } + + public bool IsInComponentTray + { + get => (_adorner == null); // adorner and alternative bounds are exclusive + } + + /// + /// Standard hit test logic that returns true if the point is contained within our bounds. This is also used to manage out mouse over state. + /// + public override Cursor GetHitTest(Point p) + { + if (_bounds.Contains(p)) + { + MouseOver = true; + return Cursors.Default; + } + MouseOver = false; + return null; + } + + /// + /// Returns an image representing the + /// + private Image GlyphImageClosed + { + get + { + if (_glyphImageClosed == null) + { + _glyphImageClosed = new Bitmap(typeof(DesignerActionGlyph), "Close_left.bmp"); + _glyphImageClosed.MakeTransparent(Color.Magenta); + if (DpiHelper.IsScalingRequired) + { + DpiHelper.ScaleBitmapLogicalToDevice(ref _glyphImageClosed); + } + } + return _glyphImageClosed; + } + } + + private Image GlyphImageOpened + { + get + { + if (_glyphImageOpened == null) + { + _glyphImageOpened = new Bitmap(typeof(DesignerActionGlyph), "Open_left.bmp"); + _glyphImageOpened.MakeTransparent(Color.Magenta); + if (DpiHelper.IsScalingRequired) + { + DpiHelper.ScaleBitmapLogicalToDevice(ref _glyphImageOpened); + } + } + return _glyphImageOpened; + } + } + + internal void InvalidateOwnerLocation() + { + if (_alternativeParent != null) + { // alternative parent and adoner are exclusive... + _alternativeParent.Invalidate(_bounds); + } + else + { + _adorner.Invalidate(_bounds); + } + } + + /// + /// Called when the state for this DesignerActionGlyph changes. Or when the related component's size or location change. Here, we re-calculate the Glyph's bounds and change our image. + /// + internal void Invalidate() + { + IComponent relatedComponent = ((DesignerActionBehavior)Behavior).RelatedComponent; + Point topRight = Point.Empty; + //handle the case that our comp is a control + if (relatedComponent is Control relatedControl && !(relatedComponent is ToolStripDropDown) && _adorner != null) + { + topRight = _adorner.BehaviorService.ControlToAdornerWindow(relatedControl); + topRight.X += relatedControl.Width; + } + // ISSUE: we can't have this special cased here - we should find a more generic approach to solving this problem special logic here for our comp being a toolstrip item + else + { + // update alternative bounds if possible... + if (_alternativeParent is ComponentTray compTray) + { + ComponentTray.TrayControl trayControl = compTray.GetTrayControlFromComponent(relatedComponent); + if (trayControl != null) + { + _alternativeBounds = trayControl.Bounds; + } + } + Rectangle newRect = DesignerUtils.GetBoundsForNoResizeSelectionType(_alternativeBounds, SelectionBorderGlyphType.Top); + topRight.X = newRect.Right; + topRight.Y = newRect.Top; + } + topRight.X -= (GlyphImageOpened.Width + CONTROLOVERLAP_X); + topRight.Y -= (GlyphImageOpened.Height - CONTROLOVERLAP_Y); + _bounds = (new Rectangle(topRight.X, topRight.Y, GlyphImageOpened.Width, GlyphImageOpened.Height)); + } + + /// + /// Used to manage the mouse-pointer-is-over-glyph state. If this is true, then we will shade our BoxImage in the Paint logic. + /// + private bool MouseOver + { + get => _mouseOver; + set + { + if (_mouseOver != value) + { + _mouseOver = value; + + InvalidateOwnerLocation(); + } + } + } + + /// + /// Responds to a paint event. This Glyph will paint its current image and, if MouseHover is true, we'll paint over the image with the 'hoverBrush'. + /// + public override void Paint(PaintEventArgs pe) + { + Image image; + if (Behavior is DesignerActionBehavior) + { + if (_insidePaint) + { + return; + } + IComponent panelComponent = ((DesignerActionUI)((DesignerActionBehavior)Behavior).ParentUI).LastPanelComponent; + IComponent relatedComponent = ((DesignerActionBehavior)Behavior).RelatedComponent; + if (panelComponent != null && panelComponent == relatedComponent) + { + image = GlyphImageOpened; + } + else + { + image = GlyphImageClosed; + } + try + { + _insidePaint = true; + pe.Graphics.DrawImage(image, _bounds.Left, _bounds.Top); + if (MouseOver || (panelComponent != null && panelComponent == relatedComponent)) + { + pe.Graphics.FillRectangle(DesignerUtils.HoverBrush, Rectangle.Inflate(_bounds, -1, -1)); + } + } + finally + { + _insidePaint = false; + } + } + } + + /// + /// Called by the ComponentTray when a tray control changes location. + /// + internal void UpdateAlternativeBounds(Rectangle newBounds) + { + _alternativeBounds = newBounds; + Invalidate(); + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DragAssistanceManager.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DragAssistanceManager.cs new file mode 100644 index 00000000000..60db5abacd8 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DragAssistanceManager.cs @@ -0,0 +1,1169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// The DragAssistanceManager, for lack of a better name, is responsible for integrating SnapLines into the DragBehavior. At the beginning of a DragBehavior this class is instantiated and at every mouse move this class is called and given the opportunity to adjust the position of the drag. The DragAssistanceManager needs to work as fast as possible - so not to interupt a drag operation. Because of this, this class has many global variables that are re-used, in hopes to limit the # of allocations per mouse move / drag operation. Also, for loops are used extensively (instead of foreach calls) to eliminate the creation of an enumerator. + /// + [SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] + internal sealed class DragAssistanceManager + { + private readonly BehaviorService _behaviorService; + private readonly IServiceProvider _serviceProvider; + private readonly Graphics _graphics; //graphics to the adornerwindow + private readonly IntPtr _rootComponentHandle; //used for mapping window points of nested controls + private Point _dragOffset; //the offset from the new drag pos compared to the last + private Rectangle _cachedDragRect; //used to store drag rect between erasing & waiting to render + private readonly Pen _edgePen = SystemPens.Highlight; + private readonly bool _disposeEdgePen = false; + private readonly Pen _baselinePen = new Pen(Color.Fuchsia); + // These are global lists of all the existing vertical and hoirizontal snaplineson the designer's surface excluding the targetControl. All SnapLine coords in theselists have been properly adjusted for the AdornerWindow coords. + private readonly ArrayList _verticalSnapLines = new ArrayList(); + private readonly ArrayList _horizontalSnapLines = new ArrayList(); + // These are SnapLines that represent our target control. + private readonly ArrayList _targetVerticalSnapLines = new ArrayList(); + private readonly ArrayList _targetHorizontalSnapLines = new ArrayList(); + // This is a list of all the different type of SnapLines our target controlhas. When compiling our global SnapLine lists, if we see a SnapLineTypethat doesn't exist on our target - we can safely ignore it + private readonly ArrayList _targetSnapLineTypes = new ArrayList(); + // These are created in our init() method (so we don't have to recreate them for every mousemove). These arrays represent the closest distance to any snap point on our target control. Once these are calculated - we can: 1) remove anything > than snapDistance and 2) determine the smallest distanceoverall + private int[] _verticalDistances; + private int[] _horizontalDistances; + //T hese are cleared and populated on every mouse move. These lists contain all the new vertical and horizontal lines we need to draw. At the end of each mouse move - these lines are stored off in the vertLines and horzLines arrays. This way - we can keep track of old snap lines and can avoid erasing and redrawing the same line. HA. + private readonly ArrayList _tempVertLines = new ArrayList(); + private readonly ArrayList _tempHorzLines = new ArrayList(); + private Line[] _vertLines = new Line[0]; + private Line[] _horzLines = new Line[0]; + // When we draw snap lines - we only draw lines from the targetControl to the control we're snapping to. To do this, we'll keep a hashtable... format: snapLineToBounds[SnapLine]=ControlBounds. + private readonly Hashtable _snapLineToBounds = new Hashtable(); + // We remember the last set of (vert & horz) lines we draw so that we can push them to the beh. svc. From there, if we receive a test hook message requesting these - we got 'em + private Line[] _recentLines; + private readonly Image _backgroundImage; //instead of calling .invalidate on the windows below us, we'll just draw over w/the background image + private const int SnapDistance = 8; //default snapping distance (pixels) + private int _snapPointX, _snapPointY; //defines the snap adjustment that needs to be made during the mousemove/drag operation + private const int INVALID_VALUE = 0x1111; //used to represent 'un-set' distances + private readonly bool _resizing; // Are we resizing? + private readonly bool _ctrlDrag; // Are we in a ctrl-drag? + + /// + /// Internal constructor called that only takes a service provider. Here it is assumed that all painting will be done to the AdornerWindow and that there are no target controsl to exclude from snapping. + /// + internal DragAssistanceManager(IServiceProvider serviceProvider) : this(serviceProvider, null, null, null, false, false) + { + } + + /// + /// Internal constructor that takes the service provider and the list of dragCompoents. + /// + internal DragAssistanceManager(IServiceProvider serviceProvider, ArrayList dragComponents) : this(serviceProvider, null, dragComponents, null, false, false) + { + } + + /// + /// Internal constructor that takes the service provider, the list of dragCompoents, and a boolean + /// indicating that we are resizing. + /// + internal DragAssistanceManager(IServiceProvider serviceProvider, ArrayList dragComponents, bool resizing) : this(serviceProvider, null, dragComponents, null, resizing, false) + { + } + + /// + /// Internal constructor called by DragBehavior. + /// + internal DragAssistanceManager(IServiceProvider serviceProvider, Graphics graphics, ArrayList dragComponents, Image backgroundImage, bool ctrlDrag) : this(serviceProvider, graphics, dragComponents, backgroundImage, false, ctrlDrag) + { + } + + /// + /// Internal constructor called by DragBehavior. + /// + [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] + internal DragAssistanceManager(IServiceProvider serviceProvider, Graphics graphics, ArrayList dragComponents, Image backgroundImage, bool resizing, bool ctrlDrag) + { + _serviceProvider = serviceProvider; + _behaviorService = serviceProvider.GetService(typeof(BehaviorService)) as BehaviorService; + if (!(serviceProvider.GetService(typeof(IDesignerHost)) is IDesignerHost host) || _behaviorService == null) + { + Debug.Fail("Cannot get DesignerHost or BehaviorService"); + return; + } + + if (graphics == null) + { + _graphics = _behaviorService.AdornerWindowGraphics; + } + else + { + _graphics = graphics; + } + + if (serviceProvider.GetService(typeof(IUIService)) is IUIService uiService) + { + //Can't use 'as' here since Color is a value type + if (uiService.Styles["VsColorSnaplines"] is Color) + { + _edgePen = new Pen((Color)uiService.Styles["VsColorSnaplines"]); + _disposeEdgePen = true; + } + + if (uiService.Styles["VsColorSnaplinesTextBaseline"] is Color) + { + _baselinePen.Dispose(); + _baselinePen = new Pen((Color)uiService.Styles["VsColorSnaplinesTextBaseline"]); + } + } + _backgroundImage = backgroundImage; + _rootComponentHandle = host.RootComponent is Control ? ((Control)host.RootComponent).Handle : IntPtr.Zero; + _resizing = resizing; + _ctrlDrag = ctrlDrag; + Initialize(dragComponents, host); + } + + /// + /// Adjusts then adds each snap line the designer has to offer to either our global horizontal and vertical lists or our target lists. Note that we also keep track of our target snapline types - 'cause we can safely ignore all other types. If valid target is false- then we don't yet know what we're snapping against - so we'll exclude the check below to skip unwanted snap line types. + /// + private void AddSnapLines(ControlDesigner controlDesigner, ArrayList horizontalList, ArrayList verticalList, bool isTarget, bool validTarget) + { + IList snapLines = controlDesigner.SnapLines; + //Used for padding snaplines + Rectangle controlRect = controlDesigner.Control.ClientRectangle; + //Used for all others + Rectangle controlBounds = controlDesigner.Control.Bounds; + // Now map the location + controlBounds.Location = controlRect.Location = _behaviorService.ControlToAdornerWindow(controlDesigner.Control); + // Remember the offset -- we need those later + int xOffset = controlBounds.Left; + int yOffset = controlBounds.Top; + + // THIS IS ONLY NEEDED FOR PADDING SNAPLINES + // We need to adjust the bounds to the client area. This is so that we don't include borders + titlebar in the snaplines. In order to add padding, we need to get the offset from the usable client area of our control and the actual origin of our control. In other words: how big is the non-client area here? Ex: we want to add padding on a form to the insides of the borders and below the titlebar. + Point offset = controlDesigner.GetOffsetToClientArea(); + controlRect.X += offset.X; //offset for non-client area + controlRect.Y += offset.Y; //offset for non-client area + + //Adjust each snapline to local coords and add it to our global list + foreach (SnapLine snapLine in snapLines) + { + if (isTarget) + { + //we will remove padding snaplines from targets - it doesn't make sense to snap to the target's padding lines + if (snapLine.Filter != null && snapLine.Filter.StartsWith(SnapLine.Padding)) + { + continue; + } + + if (validTarget && !_targetSnapLineTypes.Contains(snapLine.SnapLineType)) + { + _targetSnapLineTypes.Add(snapLine.SnapLineType); + } + } + else + { + if (validTarget && !_targetSnapLineTypes.Contains(snapLine.SnapLineType)) + { + continue; + } + // store off the bounds in our hashtable, so if we draw snaplines we know the length of the line we need to remember different bounds based on what type of snapline this is. + if ((snapLine.Filter != null) && snapLine.Filter.StartsWith(SnapLine.Padding)) + { + _snapLineToBounds.Add(snapLine, controlRect); + } + else + { + _snapLineToBounds.Add(snapLine, controlBounds); + } + } + + if (snapLine.IsHorizontal) + { + snapLine.AdjustOffset(yOffset); + horizontalList.Add(snapLine); + } + else + { + snapLine.AdjustOffset(xOffset); + verticalList.Add(snapLine); + } + } + } + + /// + /// Build up a distance array of all same-type-alignment pts to the closest point on our targetControl. Also, keep track of the smallest distance overall. + /// + private int BuildDistanceArray(ArrayList snapLines, ArrayList targetSnapLines, int[] distances, Rectangle dragBounds) + { + int smallestDistance = INVALID_VALUE; + int highestPriority = 0; + + for (int i = 0; i < snapLines.Count; i++) + { + SnapLine snapLine = (SnapLine)snapLines[i]; + if (IsMarginOrPaddingSnapLine(snapLine)) + { + // validate margin and padding snaplines (to make sure it intersects with the dragbounds) if not, skip this guy + if (!ValidateMarginOrPaddingLine(snapLine, dragBounds)) + { + distances[i] = INVALID_VALUE; + continue; + } + } + + int smallestDelta = INVALID_VALUE; //some large # + for (int j = 0; j < targetSnapLines.Count; j++) + { + SnapLine targetSnapLine = (SnapLine)targetSnapLines[j]; + + if (SnapLine.ShouldSnap(snapLine, targetSnapLine)) + { + int delta = targetSnapLine.Offset - snapLine.Offset; + if (Math.Abs(delta) < Math.Abs(smallestDelta)) + { + smallestDelta = delta; + } + } + } + + distances[i] = smallestDelta; + int pri = (int)((SnapLine)snapLines[i]).Priority; + //save off this delta for the overall smallest delta! Need to check the priority here as well if the distance is the same. E.g. smallestDistance so far is 1, for a Low snapline. We now find another distance of -1, for a Medium snapline. The old check if (Math.Abs(smallestDelta) < Math.Abs(smallestDistance)) would not set smallestDistance to -1, since the ABSOLUTE values are the same. Since the return value is used to phycially move the control, we would move the control in the direction of the Low snapline, but draw the Medium snapline in the opposite direction. + if ((Math.Abs(smallestDelta) < Math.Abs(smallestDistance)) || + ((Math.Abs(smallestDelta) == Math.Abs(smallestDistance)) && (pri > highestPriority))) + { + smallestDistance = smallestDelta; + if (pri != (int)SnapLinePriority.Always) + { + highestPriority = pri; + } + } + } + return smallestDistance; + } + + /// + /// Here, we erase all of our old horizontal and vertical snaplines UNLESS they are also contained in our tempHorzLines or tempVertLines arrays - if they are - then erasing them would be redundant (since we know we want to draw them on this mousemove) + /// + private Line[] EraseOldSnapLines(Line[] lines, ArrayList tempLines) + { + if (lines != null) + { + for (int i = 0; i < lines.Length; i++) + { + Rectangle invalidRect = Rectangle.Empty; + bool foundMatch = false; + Line line = lines[i]; + if (tempLines != null) + { + for (int j = 0; j < tempLines.Count; j++) + { + if (line.LineType != ((Line)tempLines[j]).LineType) + { + // If the lines are not the same type, then we should forcefully try to remove it. Say you have a Panel with a Button in it. By default Panel.Padding = 0, and Button.Margin = 3. As you move the button to the left, you will first get the combined LEFT margin+padding snap line. If you keep moving the button, you will now snap to the Left edge, and you will get the Blue snapline. You now move the button back to the right, and you will immediately snap to the LEFT Padding snapline. But what's gonna happen. Both the old (Left) snapline, and the LEFT Padding snapline (remember these are the panels) have the same coordinates, since Panel.Padding is 0. Thus Line.GetDiffs will return a non-null diffs. BUT e.g the first line will result in an invalidRect of (x1,y1,0,0), this we end up invalidating only a small portion of the existing Blue (left) Snapline. That's actually not okay since VERTICAL (e.g. LEFT) padding snaplines actually end up getting drawn HORIZONTALLY - thus we didn't really invalidate correctly. + continue; + } + Line[] diffs = Line.GetDiffs(line, (Line)tempLines[j]); + if (diffs != null) + { + for (int k = 0; k < diffs.Length; k++) + { + invalidRect = new Rectangle(diffs[k].x1, diffs[k].y1, diffs[k].x2 - diffs[k].x1, diffs[k].y2 - diffs[k].y1); + + invalidRect.Inflate(1, 1); + if (_backgroundImage != null) + { + _graphics.DrawImage(_backgroundImage, invalidRect, invalidRect, GraphicsUnit.Pixel); + } + else + { + _behaviorService.Invalidate(invalidRect); + } + } + foundMatch = true; + break; + } + } + } + + if (!foundMatch) + { + invalidRect = new Rectangle(line.x1, line.y1, line.x2 - line.x1, line.y2 - line.y1); + invalidRect.Inflate(1, 1); + if (_backgroundImage != null) + { + _graphics.DrawImage(_backgroundImage, invalidRect, invalidRect, GraphicsUnit.Pixel); + } + else + { + _behaviorService.Invalidate(invalidRect); + } + } + } + } + + if (tempLines != null) + { + // Now, store off all the new lines (from the temp structures), so next time around (next mousemove message) we know which lines to erase and which ones to keep + lines = new Line[tempLines.Count]; + tempLines.CopyTo(lines); + } + else + { + lines = new Line[0]; + } + return lines; + } + + internal void EraseSnapLines() + { + EraseOldSnapLines(_vertLines, null); + EraseOldSnapLines(_horzLines, null); + } + + /// + /// This internal method returns a snap line[] representing the last SnapLines that were rendered before this algorithm was stopped (usually by an OnMouseUp). This is used for storing additional toolbox drag/drop info and testing hooks. + /// + internal Line[] GetRecentLines() + { + if (_recentLines != null) + { + return _recentLines; + } + return new Line[0]; + } + + private void IdentifyAndStoreValidLines(ArrayList snapLines, int[] distances, Rectangle dragBounds, int smallestDistance) + { + int highestPriority = 1; //low + //identify top pri + for (int i = 0; i < distances.Length; i++) + { + if (distances[i] == smallestDistance) + { + int pri = (int)((SnapLine)snapLines[i]).Priority; + if ((pri > highestPriority) && (pri != (int)SnapLinePriority.Always)) + { // Always is a special category + highestPriority = pri; + } + } + } + + //store all snapLines equal to the smallest distance (of the highest priority) + for (int i = 0; i < distances.Length; i++) + { + if ((distances[i] == smallestDistance) && + (((int)((SnapLine)snapLines[i]).Priority == highestPriority) || + ((int)((SnapLine)snapLines[i]).Priority == (int)SnapLinePriority.Always))) + { //always render SnapLines with Priority.Always which has the same distance. + StoreSnapLine((SnapLine)snapLines[i], dragBounds); + } + } + } + + // Returns true of this child component (off the root control) should add its snaplines to the collection + private bool AddChildCompSnaplines(IComponent comp, ArrayList dragComponents, Rectangle clipBounds, Control targetControl) + { + if (!(comp is Control control) || //has to be a control to get snaplines + (dragComponents != null && dragComponents.Contains(comp) && !_ctrlDrag) || //cannot be something that we are dragging, unless we are in a ctrlDrag + IsChildOfParent(control, targetControl) ||//cannot be a child of the control we will drag + !clipBounds.IntersectsWith(control.Bounds) || //has to be partially visible on the rootcomp's surface + control.Parent == null || // control must have a parent. + !control.Visible) + { //control itself has to be visible -- we do mean visible, not ShadowedVisible + return false; + } + + Control c = control; + if (!c.Equals(targetControl)) + { + if (_serviceProvider.GetService(typeof(IDesignerHost)) is IDesignerHost host) + { + if (host.GetDesigner(c) is ControlDesigner controlDesigner) + { + return controlDesigner.ControlSupportsSnaplines; + } + } + } + return true; + } + + // Returns true if we should add snaplines for this control + private bool AddControlSnaplinesWhenResizing(ControlDesigner designer, Control control, Control targetControl) + { + // do not add snaplines if we are resizing the control is a container control with AutoSize set to true and the control is the parent of the targetControl + if (_resizing && + (designer is ParentControlDesigner) && + (control.AutoSize == true) && + (targetControl != null) && + (targetControl.Parent != null) && + (targetControl.Parent.Equals(control))) + { + return false; + } + return true; + } + + /// + /// Initializes our class - we cache all snap lines for every control we can find. This is done for perf. reasons. + /// + private void Initialize(ArrayList dragComponents, IDesignerHost host) + { + // our targetControl will always be the 0th component in our dragComponents array list (a.k.a. the primary selected component). + Control targetControl = null; + if (dragComponents != null && dragComponents.Count > 0) + { + targetControl = dragComponents[0] as Control; + } + Control rootControl = host.RootComponent as Control; + // the clipping bounds will be used to ignore all controls that are completely outside of our rootcomponent's bounds -this way we won't end up snapping to controls that are not visible on the form's surface + Rectangle clipBounds = new Rectangle(0, 0, rootControl.ClientRectangle.Width, rootControl.ClientRectangle.Height); + clipBounds.Inflate(-1, -1); + //determine the screen offset from our rootComponent to the AdornerWindow (since all drag notification coords will be in adorner window coords) + if (targetControl != null) + { + _dragOffset = _behaviorService.ControlToAdornerWindow(targetControl); + } + else + { + _dragOffset = _behaviorService.MapAdornerWindowPoint(rootControl.Handle, Point.Empty); + if (rootControl.Parent != null && rootControl.Parent.IsMirrored) + { + _dragOffset.Offset(-rootControl.Width, 0); + } + } + + if (targetControl != null) + { + bool disposeDesigner = false; + //get all the target snapline information we need to create one then + if (!(host.GetDesigner(targetControl) is ControlDesigner designer)) + { + designer = TypeDescriptor.CreateDesigner(targetControl, typeof(IDesigner)) as ControlDesigner; + if (designer != null) + { + //Make sure the control is not forced visible + designer.ForceVisible = false; + designer.Initialize(targetControl); + disposeDesigner = true; + } + } + AddSnapLines(designer, _targetHorizontalSnapLines, _targetVerticalSnapLines, true, targetControl != null); + if (disposeDesigner) + { + designer.Dispose(); + } + } + + //get SnapLines for all our children (nested too) off our root control + foreach (IComponent comp in host.Container.Components) + { + if (!AddChildCompSnaplines(comp, dragComponents, clipBounds, targetControl)) + { + continue; + } + + if (host.GetDesigner(comp) is ControlDesigner designer) + { + if (AddControlSnaplinesWhenResizing(designer, comp as Control, targetControl)) + { + AddSnapLines(designer, _horizontalSnapLines, _verticalSnapLines, false, targetControl != null); + } + + // Does the designer have internal control designers for which we need to add snaplines (like SplitPanelContainer, ToolStripContainer) + int numInternalDesigners = designer.NumberOfInternalControlDesigners(); + for (int i = 0; i < numInternalDesigners; i++) + { + ControlDesigner internalDesigner = designer.InternalControlDesigner(i); + if (internalDesigner != null && + AddChildCompSnaplines(internalDesigner.Component, dragComponents, clipBounds, targetControl) && + AddControlSnaplinesWhenResizing(internalDesigner, internalDesigner.Component as Control, targetControl)) + { + AddSnapLines(internalDesigner, _horizontalSnapLines, _verticalSnapLines, false, targetControl != null); + } + } + } + } + + // Now that we know how many snaplines everyone has, we can create temp arrays now. Intentionally avoiding this on every mousemove. + _verticalDistances = new int[_verticalSnapLines.Count]; + _horizontalDistances = new int[_horizontalSnapLines.Count]; + } + + /// + /// Helper function that determines if the child control is related to the parent. + /// + private static bool IsChildOfParent(Control child, Control parent) + { + if (child == null || parent == null) + { + return false; + } + Control currentParent = child.Parent; + while (currentParent != null) + { + if (currentParent.Equals(parent)) + { + return true; + } + currentParent = currentParent.Parent; + } + return false; + } + + /// + /// Helper function that identifies margin or padding snaplines + /// + private static bool IsMarginOrPaddingSnapLine(SnapLine snapLine) + { + return snapLine.Filter != null && (snapLine.Filter.StartsWith(SnapLine.Margin) || snapLine.Filter.StartsWith(SnapLine.Padding)); + } + + /// + /// Returns the offset in which the targetControl's rect needs to be re-positioned (given the direction by 'directionOffset') in order to align with the nearest possible snapline. This is called by commandSet during keyboard movements to auto-snap the control around the designer. + /// + internal Point OffsetToNearestSnapLocation(Control targetControl, IList targetSnaplines, Point directionOffset) + { + _targetHorizontalSnapLines.Clear(); + _targetVerticalSnapLines.Clear(); + //manually add our snaplines as targets + foreach (SnapLine snapline in targetSnaplines) + { + if (snapline.IsHorizontal) + { + _targetHorizontalSnapLines.Add(snapline); + } + else + { + _targetVerticalSnapLines.Add(snapline); + } + } + return OffsetToNearestSnapLocation(targetControl, directionOffset); + } + + /// + /// Returns the offset in which the targetControl's rect needs to be re-positioned (given the direction by 'directionOffset') in order to align with the nearest possible snapline. This is called by commandSet during keyboard movements to auto-snap the control around the designer. + /// + internal Point OffsetToNearestSnapLocation(Control targetControl, Point directionOffset) + { + Point offset = Point.Empty; + Rectangle currentBounds = new Rectangle(_behaviorService.ControlToAdornerWindow(targetControl), targetControl.Size); + if (directionOffset.X != 0) + {//movement somewhere in the x dir + //first, build up our distance array + BuildDistanceArray(_verticalSnapLines, _targetVerticalSnapLines, _verticalDistances, currentBounds); + //now start with the smallest distance and find the first snapline we would intercept given our horizontal direction + int minRange = directionOffset.X < 0 ? 0 : currentBounds.X; + int maxRange = directionOffset.X < 0 ? currentBounds.Right : int.MaxValue; + offset.X = FindSmallestValidDistance(_verticalSnapLines, _verticalDistances, minRange, maxRange, directionOffset.X); + if (offset.X != 0) + { + //store off the line structs for actual rendering + IdentifyAndStoreValidLines(_verticalSnapLines, _verticalDistances, currentBounds, offset.X); + if (directionOffset.X < 0) + { + offset.X *= -1; + } + } + } + if (directionOffset.Y != 0) + {//movement somewhere in the y dir + //first, build up our distance array + BuildDistanceArray(_horizontalSnapLines, _targetHorizontalSnapLines, _horizontalDistances, currentBounds); + //now start with the smallest distance and find the first snapline we would intercept given our horizontal direction + int minRange = directionOffset.Y < 0 ? 0 : currentBounds.Y; + int maxRange = directionOffset.Y < 0 ? currentBounds.Bottom : int.MaxValue; + offset.Y = FindSmallestValidDistance(_horizontalSnapLines, _horizontalDistances, minRange, maxRange, directionOffset.Y); + if (offset.Y != 0) + { + //store off the line structs for actual rendering + IdentifyAndStoreValidLines(_horizontalSnapLines, _horizontalDistances, currentBounds, offset.Y); + if (directionOffset.Y < 0) + { + offset.Y *= -1; + } + } + } + + if (!offset.IsEmpty) + { + //setup the cached info for drawing + _cachedDragRect = currentBounds; + _cachedDragRect.Offset(offset.X, offset.Y); + if (offset.X != 0) + { + _vertLines = new Line[_tempVertLines.Count]; + _tempVertLines.CopyTo(_vertLines); + } + if (offset.Y != 0) + { + _horzLines = new Line[_tempHorzLines.Count]; + _tempHorzLines.CopyTo(_horzLines); + } + } + + return offset; + } + + private static int FindSmallestValidDistance(ArrayList snapLines, int[] distances, int min, int max, int direction) + { + int distanceValue = 0; + int snapLineIndex = 0; + // loop while we still have valid distance to check and try to find the smallest valid distance + while (true) + { + // get the next smallest snapline index + snapLineIndex = SmallestDistanceIndex(distances, direction, out distanceValue); + + if (snapLineIndex == INVALID_VALUE) + { + // ran out of valid distances + break; + } + if (IsWithinValidRange(((SnapLine)snapLines[snapLineIndex]).Offset, min, max)) + { + // found it - make sure we restore the original value for rendering the snap line in the future + distances[snapLineIndex] = distanceValue; + return distanceValue; + } + } + return 0; + } + + private static bool IsWithinValidRange(int offset, int min, int max) => offset > min && offset < max; + + private static int SmallestDistanceIndex(int[] distances, int direction, out int distanceValue) + { + distanceValue = INVALID_VALUE; + int smallestIndex = INVALID_VALUE; + //check for valid array + if (distances.Length == 0) + { + return smallestIndex; + } + + //find the next smallest + for (int i = 0; i < distances.Length; i++) + { + // If a distance is 0 or if it is to our left and we're heading right or if it is to our right and we're heading left then we can null this value out + if (distances[i] == 0 || + distances[i] > 0 && direction > 0 || + distances[i] < 0 && direction < 0) + { + distances[i] = INVALID_VALUE; + } + + if (Math.Abs(distances[i]) < distanceValue) + { + distanceValue = Math.Abs(distances[i]); + smallestIndex = i; + } + } + + if (smallestIndex < distances.Length) + { + //return and clear the smallest one we found + distances[smallestIndex] = INVALID_VALUE; + } + return smallestIndex; + } + + /// + /// Actually draws the snaplines based on type, location, and specified pen + /// + private void RenderSnapLines(Line[] lines, Rectangle dragRect) + { + Pen currentPen; + for (int i = 0; i < lines.Length; i++) + { + if (lines[i].LineType == LineType.Margin || lines[i].LineType == LineType.Padding) + { + currentPen = _edgePen; + if (lines[i].x1 == lines[i].x2) + {//vertical margin + int coord = Math.Max(dragRect.Top, lines[i].OriginalBounds.Top); + coord += (Math.Min(dragRect.Bottom, lines[i].OriginalBounds.Bottom) - coord) / 2; + lines[i].y1 = lines[i].y2 = coord; + if (lines[i].LineType == LineType.Margin) + { + lines[i].x1 = Math.Min(dragRect.Right, lines[i].OriginalBounds.Right); + lines[i].x2 = Math.Max(dragRect.Left, lines[i].OriginalBounds.Left); + } + else if (lines[i].PaddingLineType == PaddingLineType.PaddingLeft) + { + lines[i].x1 = lines[i].OriginalBounds.Left; + lines[i].x2 = dragRect.Left; + } + else + { + Debug.Assert(lines[i].PaddingLineType == PaddingLineType.PaddingRight); + lines[i].x1 = dragRect.Right; + lines[i].x2 = lines[i].OriginalBounds.Right; + } + lines[i].x2--; //off by 1 adjust + } + else + {//horizontal margin + int coord = Math.Max(dragRect.Left, lines[i].OriginalBounds.Left); + coord += (Math.Min(dragRect.Right, lines[i].OriginalBounds.Right) - coord) / 2; + lines[i].x1 = lines[i].x2 = coord; + if (lines[i].LineType == LineType.Margin) + { + lines[i].y1 = Math.Min(dragRect.Bottom, lines[i].OriginalBounds.Bottom); + lines[i].y2 = Math.Max(dragRect.Top, lines[i].OriginalBounds.Top); + } + else if (lines[i].PaddingLineType == PaddingLineType.PaddingTop) + { + lines[i].y1 = lines[i].OriginalBounds.Top; + lines[i].y2 = dragRect.Top; + } + else + { + Debug.Assert(lines[i].PaddingLineType == PaddingLineType.PaddingBottom); + lines[i].y1 = dragRect.Bottom; + lines[i].y2 = lines[i].OriginalBounds.Bottom; + } + lines[i].y2--; //off by 1 adjust + } + } + else if (lines[i].LineType == LineType.Baseline) + { + currentPen = _baselinePen; + lines[i].x2 -= 1; //off by 1 adjust + } + else + { + //default to edgePen + currentPen = _edgePen; + if (lines[i].x1 == lines[i].x2) + { + lines[i].y2--; //off by 1 adjustment + } + else + { + lines[i].x2--; //off by 1 adjustment + } + } + _graphics.DrawLine(currentPen, lines[i].x1, lines[i].y1, lines[i].x2, lines[i].y2); + } + } + + /// + /// Performance improvement: Given an snapline we will render, check if it overlaps with an existing snapline. If so, combine the two. + /// + private static void CombineSnaplines(Line snapLine, ArrayList currentLines) + { + bool merged = false; + for (int i = 0; i < currentLines.Count; i++) + { + Line curLine = (Line)currentLines[i]; + Line mergedLine = Line.Overlap(snapLine, curLine); + if (mergedLine != null) + { + currentLines[i] = mergedLine; + merged = true; + } + } + if (!merged) + { + currentLines.Add(snapLine); + } + } + + /// + /// Here, we store all the SnapLines we will render. This way we can erase them when they are no longer needed. + /// + [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] + private void StoreSnapLine(SnapLine snapLine, Rectangle dragBounds) + { + Rectangle bounds = (Rectangle)_snapLineToBounds[snapLine]; + // In order for CombineSnapelines to work correctly, we have to determine the type first + LineType type = LineType.Standard; + if (IsMarginOrPaddingSnapLine(snapLine)) + { + type = snapLine.Filter.StartsWith(SnapLine.Margin) ? LineType.Margin : LineType.Padding; + } + //propagate the baseline through to the linetype + else if (snapLine.SnapLineType == SnapLineType.Baseline) + { + type = LineType.Baseline; + } + + Line line; + if (snapLine.IsVertical) + { + line = new Line(snapLine.Offset, Math.Min(dragBounds.Top + (_snapPointY != INVALID_VALUE ? _snapPointY : 0), bounds.Top), + snapLine.Offset, Math.Max(dragBounds.Bottom + (_snapPointY != INVALID_VALUE ? _snapPointY : 0), bounds.Bottom)) + { + LineType = type + }; + // Performance improvement: Check if the newly added line overlaps existing lines and if so, combine them. + CombineSnaplines(line, _tempVertLines); + } + else + { + line = new Line(Math.Min(dragBounds.Left + (_snapPointX != INVALID_VALUE ? _snapPointX : 0), bounds.Left), snapLine.Offset, + Math.Max(dragBounds.Right + (_snapPointX != INVALID_VALUE ? _snapPointX : 0), bounds.Right), snapLine.Offset) + { + LineType = type + }; + // Performance improvement: Check if the newly added line overlaps existing lines and if so, combine them. + CombineSnaplines(line, _tempHorzLines); + } + + if (IsMarginOrPaddingSnapLine(snapLine)) + { + line.OriginalBounds = bounds; + // need to know which padding line (left, right) we are storing. The original check in RenderSnapLines was wrong. It assume that the dragRect was completely within the OriginalBounds which is not necessarily true + if (line.LineType == LineType.Padding) + { + switch (snapLine.Filter) + { + case SnapLine.PaddingRight: + line.PaddingLineType = PaddingLineType.PaddingRight; + break; + case SnapLine.PaddingLeft: + line.PaddingLineType = PaddingLineType.PaddingLeft; + break; + case SnapLine.PaddingTop: + line.PaddingLineType = PaddingLineType.PaddingTop; + break; + case SnapLine.PaddingBottom: + line.PaddingLineType = PaddingLineType.PaddingBottom; + break; + default: + Debug.Fail("Unknown snapline filter type"); + break; + } + } + } + } + + /// + /// This function validates a Margin or Padding SnapLine. A valid Margin SnapLine is one that will be drawn only if the target control being dragged somehow intersects (vertically or horizontally) the coords of the given snapLine. This is done so we don't start drawing margin lines when controls are large distances apart (too much mess); + /// + [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] + private bool ValidateMarginOrPaddingLine(SnapLine snapLine, Rectangle dragBounds) + { + Rectangle bounds = (Rectangle)_snapLineToBounds[snapLine]; + if (snapLine.IsVertical) + { + if (bounds.Top < dragBounds.Top) + { + if (bounds.Top + bounds.Height < dragBounds.Top) + { + return false; + } + } + else if (dragBounds.Top + dragBounds.Height < bounds.Top) + { + return false; + } + } + else + { + if (bounds.Left < dragBounds.Left) + { + if (bounds.Left + bounds.Width < dragBounds.Left) + { + return false; + } + } + else if (dragBounds.Left + dragBounds.Width < bounds.Left) + { + return false; + } + } + //valid overlapping margin line + return true; + } + + internal Point OnMouseMove(Rectangle dragBounds, SnapLine[] snapLines) + { + bool didSnap = false; + return OnMouseMove(dragBounds, snapLines, ref didSnap, true); + } + + /// + /// Called by the DragBehavior on every mouse move. We first offset all of our drag-control's snap lines by the amount of the mouse move then follow our 2-pass heuristic to determine which SnapLines to render. + /// + internal Point OnMouseMove(Rectangle dragBounds, SnapLine[] snapLines, ref bool didSnap, bool shouldSnapHorizontally) + { + if (snapLines == null || snapLines.Length == 0) + { + return Point.Empty; + } + _targetHorizontalSnapLines.Clear(); + _targetVerticalSnapLines.Clear(); + //manually add our snaplines as targets + foreach (SnapLine snapline in snapLines) + { + if (snapline.IsHorizontal) + { + _targetHorizontalSnapLines.Add(snapline); + } + else + { + _targetVerticalSnapLines.Add(snapline); + } + } + return OnMouseMove(dragBounds, false, ref didSnap, shouldSnapHorizontally); + } + + /// + /// Called by the DragBehavior on every mouse move. We first offset all of our drag-control's snap lines by the amount of the mouse move then follow our 2-pass heuristic to determine which SnapLines to render. + /// + internal Point OnMouseMove(Rectangle dragBounds) + { + bool didSnap = false; + return OnMouseMove(dragBounds, true, ref didSnap, true); + } + + /// + /// Called by the resizebehavior. It needs to know whether we really snapped or not. The snapPoint could be (0,0) even though we snapped. + /// + internal Point OnMouseMove(Control targetControl, SnapLine[] snapLines, ref bool didSnap, bool shouldSnapHorizontally) + { + Rectangle dragBounds = new Rectangle(_behaviorService.ControlToAdornerWindow(targetControl), targetControl.Size); + didSnap = false; + return OnMouseMove(dragBounds, snapLines, ref didSnap, shouldSnapHorizontally); + } + + /// + /// Called by the DragBehavior on every mouse move. We first offset all of our drag-control's snap lines by the amount of the mouse move then follow our 2-pass heuristic to determine which SnapLines to render. + /// + private Point OnMouseMove(Rectangle dragBounds, bool offsetSnapLines, ref bool didSnap, bool shouldSnapHorizontally) + { + _tempVertLines.Clear(); + _tempHorzLines.Clear(); + _dragOffset = new Point(dragBounds.X - _dragOffset.X, dragBounds.Y - _dragOffset.Y); + if (offsetSnapLines) + { + //offset our targetSnapLines by the amount we have dragged it + for (int i = 0; i < _targetHorizontalSnapLines.Count; i++) + { + ((SnapLine)_targetHorizontalSnapLines[i]).AdjustOffset(_dragOffset.Y); + } + for (int i = 0; i < _targetVerticalSnapLines.Count; i++) + { + ((SnapLine)_targetVerticalSnapLines[i]).AdjustOffset(_dragOffset.X); + } + } + + //First pass - build up a distance array of all same-type-alignment pts to theclosest point on our targetControl. Also, keep track of the smallestdistance overall + int smallestDistanceVert = BuildDistanceArray(_verticalSnapLines, _targetVerticalSnapLines, _verticalDistances, dragBounds); + int smallestDistanceHorz = INVALID_VALUE; + if (shouldSnapHorizontally) + { + smallestDistanceHorz = BuildDistanceArray(_horizontalSnapLines, _targetHorizontalSnapLines, _horizontalDistances, dragBounds); + } + //Second Pass! We only need to do a second pass if the smallest delta is <= SnapDistance. If this is the case - then we draw snap lines for every line equal to the smallest distance available in the distance array + _snapPointX = (Math.Abs(smallestDistanceVert) <= SnapDistance) ? -smallestDistanceVert : INVALID_VALUE; + _snapPointY = (Math.Abs(smallestDistanceHorz) <= SnapDistance) ? -smallestDistanceHorz : INVALID_VALUE; + // certain behaviors (like resize) might want to know whether we really snapped or not. They can't check the returned snapPoint for (0,0) since that is a valid snapPoint. + didSnap = false; + if (_snapPointX != INVALID_VALUE) + { + IdentifyAndStoreValidLines(_verticalSnapLines, _verticalDistances, dragBounds, smallestDistanceVert); + didSnap = true; + } + + if (_snapPointY != INVALID_VALUE) + { + IdentifyAndStoreValidLines(_horizontalSnapLines, _horizontalDistances, dragBounds, smallestDistanceHorz); + didSnap = true; + } + + Point snapPoint = new Point(_snapPointX != INVALID_VALUE ? _snapPointX : 0, _snapPointY != INVALID_VALUE ? _snapPointY : 0); + Rectangle tempDragRect = new Rectangle(dragBounds.Left + snapPoint.X, dragBounds.Top + snapPoint.Y, dragBounds.Width, dragBounds.Height); + //out with the old... + _vertLines = EraseOldSnapLines(_vertLines, _tempVertLines); + _horzLines = EraseOldSnapLines(_horzLines, _tempHorzLines); + //store this drag rect - we'll use it when we are (eventually) called back on to actually render our lines + + //NOTE NOTE NOTE: If OnMouseMove is called during a resize operation, then cachedDragRect is not guaranteed to work. That is why I introduced RenderSnapLinesInternal(dragRect) + _cachedDragRect = tempDragRect; + //reset the dragoffset to this last location + _dragOffset = dragBounds.Location; + //this 'snapPoint' will be the amount we want the dragBehavior to shift the dragging control by ('cause we snapped somewhere) + return snapPoint; + } + + //NOTE NOTE NOTE: If OnMouseMove is called during a resize operation, then cachedDragRect is not guaranteed to work. That is why I introduced RenderSnapLinesInternal(dragRect) + /// + /// Called by the ResizeBehavior after it has finished drawing + /// + internal void RenderSnapLinesInternal(Rectangle dragRect) + { + _cachedDragRect = dragRect; + RenderSnapLinesInternal(); + } + + /// + /// Called by the DropSourceBehavior after it finished drawing its' draging images so that we can draw our lines on top of everything. + /// + internal void RenderSnapLinesInternal() + { + RenderSnapLines(_vertLines, _cachedDragRect); + RenderSnapLines(_horzLines, _cachedDragRect); + _recentLines = new Line[_vertLines.Length + _horzLines.Length]; + _vertLines.CopyTo(_recentLines, 0); + _horzLines.CopyTo(_recentLines, _vertLines.Length); + } + + /// + /// Clean up all of our references. + /// + internal void OnMouseUp() + { + // Here, we store off our recent snapline info to the behavior service - this is used for testing purposes + if (_behaviorService != null) + { + Line[] recent = GetRecentLines(); + string[] lines = new string[recent.Length]; + for (int i = 0; i < recent.Length; i++) + { + lines[i] = recent[i].ToString(); + } + _behaviorService.RecentSnapLines = lines; + } + EraseSnapLines(); + _graphics.Dispose(); + if (_disposeEdgePen && _edgePen != null) + { + _edgePen.Dispose(); + } + + if (_baselinePen != null) + { + _baselinePen.Dispose(); + } + + if (_backgroundImage != null) + { + _backgroundImage.Dispose(); + } + } + + /// + /// Our 'line' class - used to manage two points and calculate the difference between any two lines. + /// + internal class Line + { + public int x1, y1, x2, y2; + private LineType _lineType; + private PaddingLineType _paddingLineType; + private Rectangle _originalBounds; + + public LineType LineType + { + get => _lineType; + set => _lineType = value; + } + + + public Rectangle OriginalBounds + { + get => _originalBounds; + set => _originalBounds = value; + } + + public PaddingLineType PaddingLineType + { + get => _paddingLineType; + set => _paddingLineType = value; + } + + + public Line(int x1, int y1, int x2, int y2) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + _lineType = LineType.Standard; + } + + private Line(int x1, int y1, int x2, int y2, LineType type) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + _lineType = type; + } + + public static Line[] GetDiffs(Line l1, Line l2) + { + //x's align + if (l1.x1 == l1.x2 && l1.x1 == l2.x1) + { + return new Line[2] {new Line(l1.x1, Math.Min(l1.y1, l2.y1), l1.x1, Math.Max(l1.y1, l2.y1)), + new Line(l1.x1, Math.Min(l1.y2, l2.y2), l1.x1, Math.Max(l1.y2, l2.y2))}; + } + + //y's align + if (l1.y1 == l1.y2 && l1.y1 == l2.y1) + { + return new Line[2] {new Line(Math.Min(l1.x1, l2.x1), l1.y1, Math.Max(l1.x1, l2.x1), l1.y1), + new Line(Math.Min(l1.x2, l2.x2), l1.y1, Math.Max(l1.x2, l2.x2), l1.y1)}; + } + return null; + } + + public static Line Overlap(Line l1, Line l2) + { + // Need to be the same type + if (l1.LineType != l2.LineType) + { + return null; + } + + // only makes sense to do this for Standard and Baseline + if ((l1.LineType != LineType.Standard) && (l1.LineType != LineType.Baseline)) + { + return null; + } + + // 2 overlapping vertical lines + if ((l1.x1 == l1.x2) && (l2.x1 == l2.x2) && (l1.x1 == l2.x1)) + { + return new Line(l1.x1, Math.Min(l1.y1, l2.y1), l1.x2, Math.Max(l1.y2, l2.y2), l1.LineType); + } + + // 2 overlapping horizontal lines + if ((l1.y1 == l1.y2) && (l2.y1 == l2.y2) && (l1.y1 == l2.y2)) + { + return new Line(Math.Min(l1.x1, l2.x1), l1.y1, Math.Max(l1.x2, l2.x2), l1.y2, l1.LineType); + } + return null; + } + + public override string ToString() + { + return "Line, type = " + _lineType + ", dims =(" + x1 + ", " + y1 + ")->(" + x2 + ", " + y2 + ")"; + } + } + + /// + /// Describes different types of lines (used for margins, etc..) + /// + internal enum LineType + { + Standard, Margin, Padding, Baseline + } + + /// + /// Describes what kind of padding line we have + /// + internal enum PaddingLineType + { + None, PaddingRight, PaddingLeft, PaddingTop, PaddingBottom + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DropSourceBehavior.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DropSourceBehavior.cs new file mode 100644 index 00000000000..7a17bab8548 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DropSourceBehavior.cs @@ -0,0 +1,1458 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// The DropSourceBehavior is created by ControlDesigner when it detects that a drag operation has started. This object is passed to the BehaviorService and is used to route GiveFeedback and QueryContinueDrag drag/drop messages. In response to GiveFeedback messages, this class will render the dragging controls in real-time with the help of the DragAssistanceManager (Snaplines) object or by simply snapping to grid dots. + /// + internal sealed class DropSourceBehavior : Behavior, IComparer + { + private struct DragComponent + { + public object dragComponent; //the dragComponent + public int zorderIndex; //the dragComponent's z-order index + public Point originalControlLocation; //the original control of the control in AdornerWindow coordinates + public Point draggedLocation; //the location of the component after each drag - in AdornerWindow coordinates + public Image dragImage; //bitblt'd image of control + public Point positionOffset; //control position offset from primary selection + }; + + private DragComponent[] dragComponents; + private ArrayList dragObjects; // used to initialize the DragAssistanceManager + private BehaviorDataObject data;//drag data that represents the controls we're dragging & the effect/action + private DragDropEffects allowedEffects;//initial allowed effects for the drag operation + private DragDropEffects lastEffect;//the last effect we saw (used for determining a valid drop) + + private bool targetAllowsSnapLines;//indicates if the drop target allows snaplines (flowpanels don't for ex) + private IComponent lastDropTarget;//indicates the drop target on the last 'give feedback' event + private Point lastSnapOffset;//the last snapoffset we used. + // These 2 could be different (e.g. if dropping between forms) + private BehaviorService behaviorServiceSource;//ptr back to the BehaviorService in the drop source + private BehaviorService behaviorServiceTarget;//ptr back to the BehaviorService in the drop target + + //this object will integrate SnapLines into the drag + private DragAssistanceManager dragAssistanceManager; + + private Graphics graphicsTarget;//graphics object of the adornerwindows (via BehaviorService) in drop target + + private IServiceProvider serviceProviderSource; + private IServiceProvider serviceProviderTarget; + + private Point initialMouseLoc;//original mouse location in screen coordinates + + private Image dragImage;//A single image of the controls we are actually dragging around + private Rectangle dragImageRect;//Rectangle of the dragImage -- in SOURCE AdornerWindow coordinates + private Rectangle clearDragImageRect; //Rectangle used to remember the last dragimage rect we cleared + private Point originalDragImageLocation; //original location of the drag image + private Region dragImageRegion; + + private Point lastFeedbackLocation; // the last position we got feedback at + private Control suspendedParent;//pointer to the parent that we suspended @ the beginning of the drag + private Size parentGridSize; //used to snap around to grid dots if layoutmode == SnapToGrid + private Point parentLocation;//location of parent on AdornerWindow - used for grid snap calculations + private bool shareParent = true;//do dragged components share the parent + private bool cleanedUpDrag = false; + private StatusCommandUI statusCommandUITarget;// UI for setting the StatusBar Information in the drop target + + private IDesignerHost srcHost; + private IDesignerHost destHost; + + private bool currentShowState = true; // Initially the controls are showing + + private int primaryComponentIndex = -1; // Index of the primary component (control) in dragComponents + + /// + /// Constuctor that caches all needed vars for perf reasons. + /// + internal DropSourceBehavior(ICollection dragComponents, Control source, Point initialMouseLocation) + { + + serviceProviderSource = source.Site as IServiceProvider; + if (serviceProviderSource == null) + { + Debug.Fail("DragBehavior could not be created because the source ServiceProvider was not found"); + return; + } + + behaviorServiceSource = (BehaviorService)serviceProviderSource.GetService(typeof(BehaviorService)); + if (behaviorServiceSource == null) + { + Debug.Fail("DragBehavior could not be created because the BehaviorService was not found"); + return; + } + + if (dragComponents == null || dragComponents.Count <= 0) + { + Debug.Fail("There are no component to drag!"); + return; + } + + srcHost = (IDesignerHost)serviceProviderSource.GetService(typeof(IDesignerHost)); + if (srcHost == null) + { + Debug.Fail("DragBehavior could not be created because the srcHost could not be found"); + return; + } + + data = new BehaviorDataObject(dragComponents, source, this); + allowedEffects = DragDropEffects.Copy | DragDropEffects.None | DragDropEffects.Move; + this.dragComponents = new DragComponent[dragComponents.Count]; + parentGridSize = Size.Empty; + + lastEffect = DragDropEffects.None; + lastFeedbackLocation = new Point(-1, -1); + lastSnapOffset = Point.Empty; + dragImageRect = Rectangle.Empty; + clearDragImageRect = Rectangle.Empty; + InitiateDrag(initialMouseLocation, dragComponents); + } + + /// + /// This is the initial allowed Effect to start the drag operation with. + /// + internal DragDropEffects AllowedEffects + { + get => allowedEffects; + } + + /// + /// This is the DataObject this DropSourceBehavior represents. + /// + internal DataObject DataObject + { + get => data; + } + + /// + /// Here, during our drag operation, we need to determine the offset from the dragging control's position 'dragLoc' and the parent's grid. We'll return an offset for the image to 'snap to'. + /// + private Point AdjustToGrid(Point dragLoc) + { + //location of the drag with respect to the parent + Point controlLocation = new Point(dragLoc.X - parentLocation.X, dragLoc.Y - parentLocation.Y); + Point offset = Point.Empty; + //determine which way we need to snap + int xDelta = controlLocation.X % parentGridSize.Width; + int yDelta = controlLocation.Y % parentGridSize.Height; + // if we're more than half way to the next grid - then snap that way, otherwise snap back + if (xDelta > parentGridSize.Width / 2) + { + offset.X = parentGridSize.Width - xDelta; + } + else + { + offset.X = -xDelta; + } + + if (yDelta > parentGridSize.Height / 2) + { + offset.Y = parentGridSize.Height - yDelta; + } + else + { + offset.Y = -yDelta; + } + + return offset; + } + +#if PERFORM_AUTO_MARGINS + +/* Leaving in in case we want to re-enable this feature. The code as it is does presents major performance problems. */ + /// + /// Called after a successful drag/drop operation, this method will examine the position + /// of all the controls related (same parent) to the just-dragged control and determine + /// if they were placed within the recommended UI guidelines. If so, we'll attempt to + /// automatically adjust the margin/padding of the violators. + /// + private void AutoAdjustMargins(Control dragControl) { + if (dragControl.Parent == null) { + return; + } + + PropertyDescriptor marginProperty = TypeDescriptor.GetProperties(dragControl)["Margin"]; + //TODO: should we always adjust margins - or just when autorelocate is on??? JeffChri and FredB will think about this + //PropertyDescriptor autoRelocateProperty = TypeDescriptor.GetProperties(dragControl)["AutoRelocate"]; + if (marginProperty == null /*|| autoRelocateProperty == null*/) { + //can't do anything here + return; + } + + IDesignerHost designerHost = serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; + if (designerHost == null) { + Debug.Fail("Failed to get IDesignerHost!"); + return; + } + + IComponentChangeService changeService = serviceProvider.GetService(typeof(IComponentChangeService)) as IComponentChangeService; + + Control.ControlCollection controls = dragControl.Parent.Controls; + //loop through the controls attempting to identify which controls + //are violating others... + for (int i = 0; i < controls.Count - 1; i++) { + /* See TODO above + //control must have auto-relocate set to true + if (!(bool)autoRelocateProperty.GetValue(controls[i])) { + continue; + } + */ + for (int j = i + 1; j < controls.Count; j++) { + /* See TODO above + //this control must also have auto-relocate set to true + if (!(bool)autoRelocateProperty.GetValue(controls[j])) { + continue; + } + */ + + //check if control i and control j are violating one another... + if (DoesViolateMargin(controls[i], controls[j])) { + + //determine the new margins + Padding c1Margin = Padding.Empty; + Padding c2Margin = Padding.Empty; + SetMargins(controls[i], controls[j], ref c1Margin, ref c2Margin); + + //set the margins + using (DesignerTransaction dt = designerHost.CreateTransaction(SR.GetString(SR.AutoAdjustMargins, controls[i].Site.Name, controls[j].Site.Name))) { + if (changeService != null) { + changeService.OnComponentChanging(controls[i], marginProperty); + changeService.OnComponentChanging(controls[j], marginProperty); + } + marginProperty.SetValue(controls[i], c1Margin); + marginProperty.SetValue(controls[j], c2Margin); + + if (changeService != null) { + changeService.OnComponentChanged(controls[i], marginProperty, null, null); + changeService.OnComponentChanged(controls[j], marginProperty, null, null); + } + dt.Commit(); + } + + } + } + } + + } + + /// + /// This function determines where the two controls margins are overlapped and + // fills up two Padding structs with suggested non-overlapped values. + /// + private void SetMargins(Control c1, Control c2, ref Padding c1Margin, ref Padding c2Margin) { + //Now, perform the actual margin adjustments + // + if (c1.Bottom < c2.Top) { + //adjust the margins @ top of c1 and bottom of c2 + int marginDelta = c2.Top - c1.Bottom; + c1Margin = new Padding(c1.Margin.Left, c1.Margin.Top, c1.Margin.Right, marginDelta / 2); + c2Margin = new Padding(c2.Margin.Left, marginDelta - c1Margin.Bottom, c2.Margin.Right, c2.Margin.Bottom); + } + else if (c1.Top > c2.Bottom) { + //adjust the margins @ bottom of c2and top of c1 + int marginDelta = c1.Top - c2.Bottom; + c1Margin = new Padding(c1.Margin.Left, marginDelta / 2, c1.Margin.Right, c1.Margin.Bottom); + c2Margin = new Padding(c2.Margin.Left, c2.Margin.Top, c2.Margin.Right, marginDelta - c1Margin.Top); + } + else if (c1.Right < c2.Left) { + //adjust the margins @ left of c2and right of c1 + int marginDelta = c2.Left - c1.Right; + c1Margin = new Padding(c1.Margin.Left, c1.Margin.Top, marginDelta / 2, c1.Margin.Bottom); + c2Margin = new Padding(marginDelta - c1Margin.Right, c2.Margin.Top, c2.Margin.Right, c2.Margin.Bottom); + } + else { + //adjust the margins @ right of c2 and left of c1 + int marginDelta = c1.Left - c2.Right; + c1Margin = new Padding(marginDelta / 2, c1.Margin.Top, c1.Margin.Right , c1.Margin.Bottom); + c2Margin = new Padding(c2.Margin.Left, c2.Margin.Top, marginDelta - c1Margin.Left, c2.Margin.Bottom); + } + + } +#endif + + private Point MapPointFromSourceToTarget(Point pt) + { + if (srcHost != destHost && destHost != null) + { + pt = behaviorServiceSource.AdornerWindowPointToScreen(pt); + return behaviorServiceTarget.MapAdornerWindowPoint(IntPtr.Zero, pt); + } + else + { + return pt; + } + } + + private Point MapPointFromTargetToSource(Point pt) + { + if (srcHost != destHost && destHost != null) + { + pt = behaviorServiceTarget.AdornerWindowPointToScreen(pt); + return behaviorServiceSource.MapAdornerWindowPoint(IntPtr.Zero, pt); + } + else + { + return pt; + } + } + + /// + /// This is used to clear the drag images. + /// + private void ClearAllDragImages() + { + if (dragImageRect != Rectangle.Empty) + { + + Rectangle rect = dragImageRect; + rect.Location = MapPointFromSourceToTarget(rect.Location); + + if (graphicsTarget != null) + { + graphicsTarget.SetClip(rect); + } + + if (behaviorServiceTarget != null) + { + behaviorServiceTarget.Invalidate(rect); + } + + if (graphicsTarget != null) + { + graphicsTarget.ResetClip(); + } + } + } + +#if PERFORM_AUTO_ANCHOR + +/* Leaving in in case we want to re-enable this feature. The code as it is does presents major usability problems as + it changes anchors out from underneath you. +*/ + /// + /// This method is called after a drag operation has been completed. + /// Basically, the heuristic is: if we've just moved a control (only 1 + /// at a time) and it is aligned with another control - then check + /// the anchoring properties. If the dragged control's anchoring is + /// default - and the aligned control's anchoring has been altered + /// then set the dragged control to mimic the aligned control's + /// anchor value. + /// + private void PerformAutoAnchor(object dragControl) { + Control dragCtrl = dragControl as Control; + + if (dragCtrl == null || dragCtrl.Parent == null) { + //dragged object is not a control or there is no valid panent + return; + } + + PropertyDescriptor dragControlAnchorProp = TypeDescriptor.GetProperties(dragCtrl)["Anchor"]; + if (dragControlAnchorProp == null) { + //couldn't get anhor prop + return; + } + + DefaultValueAttribute defAttr = (DefaultValueAttribute)dragControlAnchorProp.Attributes[typeof(DefaultValueAttribute)]; + if (defAttr != null && (AnchorStyles)defAttr.Value != (AnchorStyles)dragControlAnchorProp.GetValue(dragCtrl)) { + //dragged control's anchor prop was not default + return; + } + + AnchorStyles dragControlAnchor = (AnchorStyles)dragControlAnchorProp.GetValue(dragCtrl); + Rectangle dragControlBounds = dragCtrl.Bounds; + + //begin a search to find a control the dragControl is aligned with. We'll + //stop looking as soon as we find a control that aligns with us and have a + //different anchor value + foreach (Control c in dragCtrl.Parent.Controls) { + if (c.Equals(dragCtrl)) { + //skip if this is the dragged control + continue; + } + + Rectangle bounds = c.Bounds; + + //look for a friendly aligned child control + if (c.Left == dragControlBounds.Left || c.Right == dragControlBounds.Right || + c.Top == dragControlBounds.Top || c.Bottom == dragControlBounds.Bottom) { + + PropertyDescriptor anchorProp = TypeDescriptor.GetProperties(c)["Anchor"]; + if (anchorProp != null) { + //cache off the anchorstyle for this aligned child control + AnchorStyles style = (AnchorStyles)anchorProp.GetValue(c); + + if (style != dragControlAnchor) { + + //here, the aligned child control's anchor prop is + //different from our dragcontrol! So we'll create a transaction + //and set the dragControl to the same style. + IDesignerHost host = serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; + if (host == null) { + //couldn't find a designer host + return; + } + + using (DesignerTransaction dt = host.CreateTransaction(SR.GetString(SR.PerformAutoAnchor, c.Site.Name, dragCtrl.Site.Name))) { + dragControlAnchorProp.SetValue(dragControl, style); + dt.Commit(); + break; + } + } + } + } + } + } + +#endif + +#if PERFORM_AUTO_MARGINS + /// + /// Returns true if c1 and c2 are within each others margins + /// but not overlapping. + /// + private bool DoesViolateMargin(Control c1, Control c2) { + //if the margin rects intersect and the and the actual + //control bounds do not - then there's a violation + // + return !(Rectangle.Intersect(DesignerUtils.GetMarginBounds(c1), DesignerUtils.GetMarginBounds(c2)).IsEmpty) && + (Rectangle.Intersect(c1.Bounds, c2.Bounds).IsEmpty); + } +#endif + + // Yeah this is recursive, but we also need to resite all + // the children of this control, and their children, and their children... + private void SetDesignerHost(Control c) + { + foreach (Control control in c.Controls) + { + SetDesignerHost(control); + } + if (c.Site != null && !(c.Site is INestedSite) && destHost != null) + { + destHost.Container.Add(c); + } + } + + private void DropControl(int dragComponentIndex, Control dragTarget, Control dragSource, bool localDrag) + { + Control currentControl = dragComponents[dragComponentIndex].dragComponent as Control; + if (lastEffect == DragDropEffects.Copy || (srcHost != destHost && destHost != null)) + { + //between forms or copy + currentControl.Visible = true; + bool visibleState = true; + PropertyDescriptor propLoc = TypeDescriptor.GetProperties(currentControl)["Visible"]; + if (propLoc != null) + { + // store off the visible state. When adding the control to the new designer host, a new control designer will be created for the control. Since currentControl.Visible is currently FALSE (See InitiateDrag), the shadowed Visible property will be FALSE as well. This is not what we want. + visibleState = (bool)propLoc.GetValue(currentControl); + } + + // Hook the control to its new designerHost + SetDesignerHost(currentControl); + currentControl.Parent = dragTarget; + if (propLoc != null) + { + //Make sure and set the Visible property to the correct value + propLoc.SetValue(currentControl, visibleState); + } + } + else if (!localDrag && currentControl.Parent.Equals(dragSource)) + { + //between containers + dragSource.Controls.Remove(currentControl); + currentControl.Visible = true; + dragTarget.Controls.Add(currentControl); + } + } + + private void SetLocationPropertyAndChildIndex(int dragComponentIndex, Control dragTarget, Point dropPoint, int newIndex, bool allowSetChildIndexOnDrop) + { + PropertyDescriptor propLoc = TypeDescriptor.GetProperties(dragComponents[dragComponentIndex].dragComponent)["Location"]; + if ((propLoc != null) && (dragComponents[dragComponentIndex].dragComponent is Control currentControl)) + { + // ControlDesigner shadows the Location property. If the control is parented and the parent is a scrollable control, then it expects the Location to be in displayrectangle coordinates. At this point bounds are in clientrectangle coordinates, so we need to check if we need to adjust the coordinates. + Point pt = new Point(dropPoint.X, dropPoint.Y); + if (currentControl.Parent is ScrollableControl p) + { + Point ptScroll = p.AutoScrollPosition; + pt.Offset(-ptScroll.X, -ptScroll.Y); //always want to add the control below/right of the AutoScrollPosition + } + + propLoc.SetValue(currentControl, pt); + // In some cases the target designer wants to maintain its own ZOrder, in that case we shouldn't try and set the childindex. FlowLayoutPanelDesigner is one such case. + if (allowSetChildIndexOnDrop) + { + dragTarget.Controls.SetChildIndex(currentControl, newIndex); + } + } + } + + /// + /// This is where we end the drag and commit the new control locations. To do this correctly, we loop through every control and find its propertyDescriptor for the Location. Then call SetValue(). After this we re-enable the adorners. Finally, we pop ourselves from the BehaviorStack. + /// + private void EndDragDrop(bool allowSetChildIndexOnDrop) + { + if (!(data.Target is Control dragTarget)) + { + return; //can't deal with a non-control drop target yet + } + + // If for some reason we couldn't get these guys, let's try and get them here + if (serviceProviderTarget == null) + { + Debug.Fail("EndDragDrop - how can serviceProviderTarget be null?"); + serviceProviderTarget = dragTarget.Site as IServiceProvider; + if (serviceProviderTarget == null) + { + Debug.Fail("EndDragDrop - how can serviceProviderTarget be null?"); + return; + } + } + + if (destHost == null) + { + Debug.Fail("EndDragDrop - how can destHost be null?"); + destHost = (IDesignerHost)serviceProviderTarget.GetService(typeof(IDesignerHost)); + if (destHost == null) + { + Debug.Fail("EndDragDrop - how can destHost be null?"); + return; + } + } + + if (behaviorServiceTarget == null) + { + Debug.Fail("EndDragDrop - how can behaviorServiceTarget be null?"); + behaviorServiceTarget = (BehaviorService)serviceProviderTarget.GetService(typeof(BehaviorService)); + if (behaviorServiceTarget == null) + { + Debug.Fail("EndDragDrop - how can behaviorServiceTarget be null?"); + return; + } + } + +#if PERFORM_AUTO_STUFF + bool successfulDrag = false; +#endif + // We use this list when doing a Drag-Copy, so that we can correctly restore state when we are done. See Copy code below. + ArrayList originalControls = null; + bool performCopy = (lastEffect == DragDropEffects.Copy); + + Control dragSource = data.Source; + bool localDrag = dragSource.Equals(dragTarget); + PropertyDescriptor targetProp = TypeDescriptor.GetProperties(dragTarget)["Controls"]; + PropertyDescriptor sourceProp = TypeDescriptor.GetProperties(dragSource)["Controls"]; + IComponentChangeService componentChangeSvcSource = (IComponentChangeService)serviceProviderSource.GetService(typeof(IComponentChangeService)); + IComponentChangeService componentChangeSvcTarget = (IComponentChangeService)serviceProviderTarget.GetService(typeof(IComponentChangeService)); + + if (dragAssistanceManager != null) + { + dragAssistanceManager.OnMouseUp(); + } + + // If we are dropping between hosts, we want to set the selection in the new host to be the components that we are dropping. VSWhidbey# 395676 ... or if we are copying + ISelectionService selSvc = null; + if (performCopy || (srcHost != destHost && destHost != null)) + { + selSvc = (ISelectionService)serviceProviderTarget.GetService(typeof(ISelectionService)); + } + + try + { + if (dragComponents != null && dragComponents.Length > 0) + { + DesignerTransaction transSource = null; + DesignerTransaction transTarget = null; + string transDesc; + if (dragComponents.Length == 1) + { + string name = TypeDescriptor.GetComponentName(dragComponents[0].dragComponent); + if (name == null || name.Length == 0) + { + name = dragComponents[0].dragComponent.GetType().Name; + } + transDesc = string.Format(performCopy ? SR.BehaviorServiceCopyControl : SR.BehaviorServiceMoveControl, name); + } + else + { + transDesc = string.Format(performCopy ? SR.BehaviorServiceCopyControls : SR.BehaviorServiceMoveControls, dragComponents.Length); + } + + // We don't want to create a transaction in the source, if we are doing a cross-form copy + if (srcHost != null && !(srcHost != destHost && destHost != null && performCopy)) + { + transSource = srcHost.CreateTransaction(transDesc); + } + + if (srcHost != destHost && destHost != null) + { + transTarget = destHost.CreateTransaction(transDesc); + } + + try + { + ComponentTray tray = null; + int numberOfOriginalTrayControls = 0; + // If we are copying the controls, then, well, let's make a copy of'em... We then stuff the copy into the dragComponents array, since that keeps the rest of this code the same... No special casing needed. + if (performCopy) + { + // As part of a Ctrl-Drag, components might have been added to the component tray, make sure that their location gets updated as well (think ToolStrips). Get the current number of controls in the Component Tray in the target + tray = serviceProviderTarget.GetService(typeof(ComponentTray)) as ComponentTray; + numberOfOriginalTrayControls = tray != null ? tray.Controls.Count : 0; + + // Get the objects to copy + ArrayList temp = new ArrayList(); + for (int i = 0; i < dragComponents.Length; i++) + { + temp.Add(dragComponents[i].dragComponent); + } + + // Create a copy of them + temp = DesignerUtils.CopyDragObjects(temp, serviceProviderTarget) as ArrayList; + if (temp == null) + { + Debug.Fail("Couldn't create copies of the controls we are dragging."); + return; + } + + originalControls = new ArrayList(); + // And stick the copied controls back into the dragComponents array + for (int j = 0; j < temp.Count; j++) + { + // ... but save off the old controls first + originalControls.Add(dragComponents[j].dragComponent); + dragComponents[j].dragComponent = temp[j]; + } + } + + if ((!localDrag || performCopy) && componentChangeSvcSource != null && componentChangeSvcTarget != null) + { + componentChangeSvcTarget.OnComponentChanging(dragTarget, targetProp); + // If we are performing a copy, then the dragSource will not change + if (!performCopy) + { + componentChangeSvcSource.OnComponentChanging(dragSource, sourceProp); + } + } + + // We need to calculate initialDropPoint first to be able to calculate the new drop point for all controls Need to drop it first to make sure that the Parent gets set correctly. + DropControl(primaryComponentIndex, dragTarget, dragSource, localDrag); + Point initialDropPoint = behaviorServiceSource.AdornerWindowPointToScreen(dragComponents[primaryComponentIndex].draggedLocation); + + // Tricky... initialDropPoint is the dropPoint in the source adornerwindow, which could be different than the target adornerwindow. But since we first convert it to screen coordinates, and then to client coordinates using the new parent, we end up dropping in the right spot. Cool, huh! + initialDropPoint = ((Control)dragComponents[primaryComponentIndex].dragComponent).Parent.PointToClient(initialDropPoint); + + // Correct (only) the drop point for when Parent is mirrored, then use the offsets for the other controls, which were already corrected for mirroring in InitDrag + if (((Control)(dragComponents[primaryComponentIndex].dragComponent)).Parent.IsMirrored) + { + initialDropPoint.Offset(-((Control)(dragComponents[primaryComponentIndex].dragComponent)).Width, 0); + } + + // check permission to do that + Control primaryComponent = dragComponents[primaryComponentIndex].dragComponent as Control; + PropertyDescriptor propLoc = TypeDescriptor.GetProperties(primaryComponent)["Location"]; + if (primaryComponent != null && propLoc != null) + { + try + { + componentChangeSvcTarget.OnComponentChanging(primaryComponent, propLoc); + } + + catch (CheckoutException coEx) + { + if (coEx == CheckoutException.Canceled) + { + return; + } + throw; + } + } + // everything is fine, carry on... + SetLocationPropertyAndChildIndex(primaryComponentIndex, dragTarget, initialDropPoint, + shareParent ? dragComponents[primaryComponentIndex].zorderIndex : 0, allowSetChildIndexOnDrop); + if (selSvc != null) + { + selSvc.SetSelectedComponents(new object[] { dragComponents[primaryComponentIndex].dragComponent }, SelectionTypes.Primary | SelectionTypes.Replace); + } + + for (int i = 0; i < dragComponents.Length; i++) + { + if (i == primaryComponentIndex) + { + // did this one above + continue; + } + + DropControl(i, dragTarget, dragSource, localDrag); + Point dropPoint = new Point(initialDropPoint.X + dragComponents[i].positionOffset.X, + initialDropPoint.Y + dragComponents[i].positionOffset.Y); + SetLocationPropertyAndChildIndex(i, dragTarget, dropPoint, + shareParent ? dragComponents[i].zorderIndex : 0, allowSetChildIndexOnDrop); + if (selSvc != null) + { + selSvc.SetSelectedComponents(new object[] { dragComponents[i].dragComponent }, SelectionTypes.Add); + } + + } + + if ((!localDrag || performCopy) && componentChangeSvcSource != null && componentChangeSvcTarget != null) + { + componentChangeSvcTarget.OnComponentChanged(dragTarget, targetProp, dragTarget.Controls, dragTarget.Controls); + if (!performCopy) + { + componentChangeSvcSource.OnComponentChanged(dragSource, sourceProp, dragSource.Controls, dragSource.Controls); + } + } + + // If we did a Copy, then restore the old controls to make sure we set state correctly + if (originalControls != null) + { + for (int i = 0; i < originalControls.Count; i++) + { + dragComponents[i].dragComponent = originalControls[i]; + } + originalControls = null; + } + + // Rearrange the Component Tray - if we have to + if (performCopy) + { + if (tray == null) + { + // the target did not have a tray already, so let's go get it - if there is one + tray = serviceProviderTarget.GetService(typeof(ComponentTray)) as ComponentTray; + } + + if (tray != null) + { + int numberOfTrayControlsAdded = tray.Controls.Count - numberOfOriginalTrayControls; + + if (numberOfTrayControlsAdded > 0) + { + ArrayList listOfTrayControls = new ArrayList(); + for (int i = 0; i < numberOfTrayControlsAdded; i++) + { + listOfTrayControls.Add(tray.Controls[numberOfOriginalTrayControls + i]); + } + tray.UpdatePastePositions(listOfTrayControls); + } + } + } + + // We need to CleanupDrag BEFORE we commit the transaction. The reason is that cleaning up can potentially cause a layout, and then any changes that happen due to the layout would be in a separate UndoUnit. We want the D&D to be undoable in one step. + CleanupDrag(false); + if (transSource != null) + { + transSource.Commit(); + transSource = null; +#if PERFORM_AUTO_STUFF + successfulDrag = true; +#endif + } + if (transTarget != null) + { + transTarget.Commit(); + transTarget = null; + } + } + + finally + { + if (transSource != null) + { + transSource.Cancel(); + } + + if (transTarget != null) + { + transTarget.Cancel(); + } + } + } + } + finally + { + // If we did a Copy, then restore the old controls to make sure we set state correctly + if (originalControls != null) + { + for (int i = 0; i < originalControls.Count; i++) + { + dragComponents[i].dragComponent = originalControls[i]; + } + } + + // Even though we call CleanupDrag(false) twice (see above), this method guards against doing the wrong thing. + CleanupDrag(false); + if (statusCommandUITarget != null) + { + // if selSvs is not null, then we either did a copy, or moved between forms, so use it to set the right info + statusCommandUITarget.SetStatusInformation(selSvc == null ? dragComponents[primaryComponentIndex].dragComponent as Component : + selSvc.PrimarySelection as Component); + } + } + +#if PERFORM_AUTO_STUFF + + if (successfulDrag) { + +#if PERFORM_AUTO_MARGINS + //auto adjust margin values of controls if they were + //placed within the recommended UI distances... + AutoAdjustMargins(dragComponents[primaryComponentIndex].dragComponent as Control); +#endif + +#if PERFORM_AUTO_ANCHOR + + //if we aligned a single control with another that has + //non-default anchoring props, then we will apply those + //values to the control that we just dragged. + if (dragComponents.Length == 1) { + PerformAutoAnchor(dragComponents[0].dragComponent); + } +#endif + } +#endif + // clear the last feedback loc + lastFeedbackLocation = new Point(-1, -1); + } + + /// + /// Called by the BehaviorService when the GiveFeedback event is fired. Here, we attempt to render all of our dragging control snapshots. *After, of course, we let the DragAssistanceManager adjust the position due to any SnapLine activity. + /// + internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) + { + // cache off this last effect so in QueryContinueDrag we can identify (if dropped) a valid drop operation + lastEffect = e.Effect; + //if our target is null, we can't drop anywhere, so don't even draw images + if (data.Target == null || e.Effect == DragDropEffects.None) + { + if (clearDragImageRect != dragImageRect) + { + // To avoid flashing, we only want to clear the drag images if the the dragimagerect is different than the last time we got here. I.e. if we keep dragging over an area where we are not allowed to drop, then we only have to clear the dragimages once. + ClearAllDragImages(); + clearDragImageRect = dragImageRect; + } + if (dragAssistanceManager != null) + { + dragAssistanceManager.EraseSnapLines(); + } + return; + } + + bool createNewDragAssistance = false; + Point mouseLoc = Control.MousePosition; + bool altKeyPressed = Control.ModifierKeys == Keys.Alt; + if (altKeyPressed && dragAssistanceManager != null) + { + //erase any snaplines (if we had any) + dragAssistanceManager.EraseSnapLines(); + } + + // I can't get rid of the ole-drag/drop default cursor that show's the cross-parent drag indication + if (data.Target.Equals(data.Source) && lastEffect != DragDropEffects.Copy) + { + e.UseDefaultCursors = false; + Cursor.Current = Cursors.Default; + } + else + { + e.UseDefaultCursors = true; + } + + // only do this drawing when the mouse pointer has actually moved so we don't continuously redraw and flicker like mad. + Control target = data.Target as Control; + if ((mouseLoc != lastFeedbackLocation) || (altKeyPressed && dragAssistanceManager != null)) + { + if (!data.Target.Equals(lastDropTarget)) + { + serviceProviderTarget = target.Site as IServiceProvider; + if (serviceProviderTarget == null) + { + return; + } + + IDesignerHost newDestHost = (IDesignerHost)serviceProviderTarget.GetService(typeof(IDesignerHost)); + if (newDestHost == null) + { + return; + } + + targetAllowsSnapLines = true; + //check to see if the current designer participate with SnapLines + if (newDestHost.GetDesigner(target) is ControlDesigner designer && !designer.ParticipatesWithSnapLines) + { + targetAllowsSnapLines = false; + } + + statusCommandUITarget = new StatusCommandUI(serviceProviderTarget); + // Spin up new stuff if the host changes, or if this is the first time through (lastDropTarget will be null in this case) + if ((lastDropTarget == null) || (newDestHost != destHost)) + { + if (destHost != null && destHost != srcHost) + { + // re-enable all glyphs in the old host... need to do this before we get the new behaviorservice + behaviorServiceTarget.EnableAllAdorners(true); + } + + behaviorServiceTarget = (BehaviorService)serviceProviderTarget.GetService(typeof(BehaviorService)); + if (behaviorServiceTarget == null) + { + return; + } + + GetParentSnapInfo(target, behaviorServiceTarget); + + // Disable the adorners in the new host, but only if this is not the source host, since that will already have been done + if (newDestHost != srcHost) + { + DisableAdorners(serviceProviderTarget, behaviorServiceTarget, true); + } + + // clear the old drag images in the old graphicsTarget + ClearAllDragImages(); + + // Build a new dragImageRegion -- but only if we are changing hosts + if (lastDropTarget != null) + { + for (int i = 0; i < dragObjects.Count; i++) + { + Control dragControl = (Control)dragObjects[i]; + Rectangle controlRect = behaviorServiceSource.ControlRectInAdornerWindow(dragControl); + // Can't call MapPointFromSourceToTarget since we always want to do this + controlRect.Location = behaviorServiceSource.AdornerWindowPointToScreen(controlRect.Location); + controlRect.Location = behaviorServiceTarget.MapAdornerWindowPoint(IntPtr.Zero, controlRect.Location); + if (i == 0) + { + if (dragImageRegion != null) + { + dragImageRegion.Dispose(); + } + dragImageRegion = new Region(controlRect); + } + else + { + dragImageRegion.Union(controlRect); + } + } + } + + if (graphicsTarget != null) + { + graphicsTarget.Dispose(); + } + graphicsTarget = behaviorServiceTarget.AdornerWindowGraphics; + + // Always force the dragassistance manager to be created in this case. + createNewDragAssistance = true; + destHost = newDestHost; + } + lastDropTarget = data.Target; + } + + if (ShowHideDragControls(lastEffect == DragDropEffects.Copy) && !createNewDragAssistance) + { + createNewDragAssistance = true; + } + + // Create new dragassistancemanager if needed + if (createNewDragAssistance && behaviorServiceTarget.UseSnapLines) + { + if (dragAssistanceManager != null) + { + //erase any snaplines (if we had any) + dragAssistanceManager.EraseSnapLines(); + } + dragAssistanceManager = new DragAssistanceManager(serviceProviderTarget, graphicsTarget, dragObjects, null, lastEffect == DragDropEffects.Copy); + } + + //The new position of the primary control, i.e. where did we just drag it to + Point newPosition = new Point(mouseLoc.X - initialMouseLoc.X + dragComponents[primaryComponentIndex].originalControlLocation.X, + mouseLoc.Y - initialMouseLoc.Y + dragComponents[primaryComponentIndex].originalControlLocation.Y); + // Map it to the target's adorner window so that we can snap correctly + newPosition = MapPointFromSourceToTarget(newPosition); + //The new rectangle + Rectangle newRect = new Rectangle(newPosition.X, newPosition.Y, + dragComponents[primaryComponentIndex].dragImage.Width, + dragComponents[primaryComponentIndex].dragImage.Height); + //if we have a valid snapline engine - ask it to offset our drag + if (dragAssistanceManager != null) + { + if (targetAllowsSnapLines && !altKeyPressed) + { + // Remembering the last snapoffset allows us to correctly erase snaplines, if the user subsequently holds down the Alt-Key. Remember that we don't physically move the mouse, we move the control (or rather the image of the control). So if we didn't remember the last snapoffset and the user then hit the Alt-Key, we would actually redraw the control at the actual mouse location, which would make the control "jump" which is not what the user would expect. Why does the control "jump"? Because when a control is snapped, we have offset the control relative to where the mouse is, but we have not update the physical mouse position. When the user hits the Alt-Key they expect the control to be where it was (whether snapped or not). + lastSnapOffset = dragAssistanceManager.OnMouseMove(newRect); + } + else + { + dragAssistanceManager.OnMouseMove(new Rectangle(-100, -100, 0, 0));/*just an invalid rect - so we won't snap*///); + } + } + //if we know our parent is forcing grid sizes + else if (!parentGridSize.IsEmpty) + { + lastSnapOffset = AdjustToGrid(newPosition); + } + + // Set the new location after the drag (only need to do this for the primary control) adjusted for a snap offset + newPosition.X += lastSnapOffset.X; + newPosition.Y += lastSnapOffset.Y; + + // draggedLocation is the coordinates in the source AdornerWindow. Need to do this since our original location is in those coordinates + dragComponents[primaryComponentIndex].draggedLocation = MapPointFromTargetToSource(newPosition); + + // Now draw the dragImage in the correct location + // FIRST, INVALIDATE THE REGION THAT IS OUTSIDE OF THE DRAGIMAGERECT + // First remember the old rect so that we can invalidate the right thing + Rectangle previousImageRect = dragImageRect; + // This is in Source adorner window coordinates + newPosition = new Point(mouseLoc.X - initialMouseLoc.X + originalDragImageLocation.X, + mouseLoc.Y - initialMouseLoc.Y + originalDragImageLocation.Y); + newPosition.X += lastSnapOffset.X; + newPosition.Y += lastSnapOffset.Y; + // Store this off in Source adornerwindow coordinates + dragImageRect.Location = newPosition; + + previousImageRect.Location = MapPointFromSourceToTarget(previousImageRect.Location); + Rectangle newImageRect = dragImageRect; + newImageRect.Location = MapPointFromSourceToTarget(newImageRect.Location); + + Rectangle unionRectangle = Rectangle.Union(newImageRect, previousImageRect); + Region invalidRegion = new Region(unionRectangle); + invalidRegion.Exclude(newImageRect); + + // SECOND, INVALIDATE THE TRANSPARENT REGION OF THE DRAGIMAGERECT + using (Region invalidDragRegion = dragImageRegion.Clone()) + { + invalidDragRegion.Translate(mouseLoc.X - initialMouseLoc.X + lastSnapOffset.X, mouseLoc.Y - initialMouseLoc.Y + lastSnapOffset.Y); + invalidDragRegion.Complement(newImageRect); + invalidDragRegion.Union(invalidRegion); +#if DEBUGDROPSOURCE + System.Threading.Thread.Sleep(750); + graphicsTarget.FillRegion(Brushes.Red, invalidDragRegion); + System.Threading.Thread.Sleep(750); +#endif + behaviorServiceTarget.Invalidate(invalidDragRegion); + } + invalidRegion.Dispose(); + if (graphicsTarget != null) + { + graphicsTarget.SetClip(newImageRect); + graphicsTarget.DrawImage(dragImage, newImageRect.X, newImageRect.Y); + graphicsTarget.ResetClip(); + } + + if (dragComponents[primaryComponentIndex].dragComponent is Control c) + { + // update drag position on the status bar + Point dropPoint = behaviorServiceSource.AdornerWindowPointToScreen(dragComponents[primaryComponentIndex].draggedLocation); + dropPoint = target.PointToClient(dropPoint); + // must adjust offsets for the flipped X axis when our container and control are mirrored + if (target.IsMirrored && c.IsMirrored) + { + dropPoint.Offset(-c.Width, 0); + } + if (statusCommandUITarget != null) + { + statusCommandUITarget.SetStatusInformation(c as Component, dropPoint); + } + } + + // allow any snaplines to be drawn above our drag images as long as the alt key is not pressed and the mouse is over the root comp + if (dragAssistanceManager != null && !altKeyPressed && targetAllowsSnapLines) + { + dragAssistanceManager.RenderSnapLinesInternal(); + } + + // save off the current mouse position + lastFeedbackLocation = mouseLoc; + } + data.Target = null; + } + + /// + /// We want to sort the dragComponents in descending z-order. We want to make sure that we draw the control lowest in the z-order first, and drawing the control at the top of the z-order last. Remember that z-order indices are in reverse order. I.e. the control that is at the top of the z-order list has the lowest z-order index. + /// + int IComparer.Compare(object x, object y) + { + DragComponent dc1 = (DragComponent)x; + DragComponent dc2 = (DragComponent)y; + if (dc1.zorderIndex > dc2.zorderIndex) + { + return -1; + } + else if (dc1.zorderIndex < dc2.zorderIndex) + { + return 1; + } + else + { + return 0; + } + } + + private void GetParentSnapInfo(Control parentControl, BehaviorService bhvSvc) + { + // Clear out whatever value we might have had stored off + parentGridSize = Size.Empty; + if (bhvSvc != null && !bhvSvc.UseSnapLines) + { + PropertyDescriptor snapProp = TypeDescriptor.GetProperties(parentControl)["SnapToGrid"]; + if (snapProp != null && (bool)snapProp.GetValue(parentControl)) + { + PropertyDescriptor gridProp = TypeDescriptor.GetProperties(parentControl)["GridSize"]; + if (gridProp != null) + { + //cache of the gridsize and the location of the parent on the adornerwindow + if (dragComponents[primaryComponentIndex].dragComponent is Control) + { + parentGridSize = (Size)gridProp.GetValue(parentControl); + parentLocation = bhvSvc.MapAdornerWindowPoint(parentControl.Handle, Point.Empty); + if (parentControl.Parent != null && parentControl.Parent.IsMirrored) + { + parentLocation.Offset(-parentControl.Width, 0); + } + } + } + } + } + } + + private void DisableAdorners(IServiceProvider serviceProvider, BehaviorService behaviorService, bool hostChange) + { + // find our bodyglyph adorner offered by the behavior service we don't want to disable the transparent body glyphs + Adorner bodyGlyphAdorner = null; + SelectionManager selMgr = (SelectionManager)serviceProvider.GetService(typeof(SelectionManager)); + if (selMgr != null) + { + bodyGlyphAdorner = selMgr.BodyGlyphAdorner; + } + + //disable all adorners except for bodyglyph adorner + foreach (Adorner a in behaviorService.Adorners) + { + if (bodyGlyphAdorner != null && a.Equals(bodyGlyphAdorner)) + { + continue; + } + a.EnabledInternal = false; + } + behaviorService.Invalidate(); + + if (hostChange) + { + selMgr.OnBeginDrag(new BehaviorDragDropEventArgs(dragObjects)); + } + } + + /// + /// Called when the ContolDesigner starts a drag operation. Here, all adorners are disabled, screen shots of all related controls are taken, and the DragAssistanceManager (for SnapLines) is created. + /// + private void InitiateDrag(Point initialMouseLocation, ICollection dragComps) + { + dragObjects = new ArrayList(dragComps); + DisableAdorners(serviceProviderSource, behaviorServiceSource, false); + Control primaryControl = dragObjects[0] as Control; + Control primaryParent = primaryControl?.Parent; + Color backColor = primaryParent != null ? primaryParent.BackColor : Color.Empty; + dragImageRect = Rectangle.Empty; + clearDragImageRect = Rectangle.Empty; + initialMouseLoc = initialMouseLocation; + + //loop through every control we need to drag, calculate the offsets and get a snapshot + for (int i = 0; i < dragObjects.Count; i++) + { + Control dragControl = (Control)dragObjects[i]; + + dragComponents[i].dragComponent = dragObjects[i]; + dragComponents[i].positionOffset = new Point(dragControl.Location.X - primaryControl.Location.X, + dragControl.Location.Y - primaryControl.Location.Y); + Rectangle controlRect = behaviorServiceSource.ControlRectInAdornerWindow(dragControl); + if (dragImageRect.IsEmpty) + { + dragImageRect = controlRect; + dragImageRegion = new Region(controlRect); + } + else + { + dragImageRect = Rectangle.Union(dragImageRect, controlRect); + dragImageRegion.Union(controlRect); + } + + //Initialize the dragged location to be the current position of the control + dragComponents[i].draggedLocation = controlRect.Location; + dragComponents[i].originalControlLocation = dragComponents[i].draggedLocation; + //take snapshot of each control + DesignerUtils.GenerateSnapShot(dragControl, ref dragComponents[i].dragImage, i == 0 ? 2 : 1, 1, backColor); + + // The dragged components are not in any specific order. If they all share the same parent, we will sort them by their index in that parent's control's collection to preserve correct Z-order + if (primaryParent != null && shareParent) + { + dragComponents[i].zorderIndex = primaryParent.Controls.GetChildIndex(dragControl, false /*throwException*/); + if (dragComponents[i].zorderIndex == -1) + { + shareParent = false; + } + } + } + if (shareParent) + { + Array.Sort(dragComponents, this); + } + + // Now that we are sorted, set the primaryComponentIndex... + for (int i = 0; i < dragComponents.Length; i++) + { + if (primaryControl.Equals(dragComponents[i].dragComponent as Control)) + { + primaryComponentIndex = i; + break; + } + } + + Debug.Assert(primaryComponentIndex != -1, "primaryComponentIndex was not set!"); + //suspend layout of the parent + if (primaryParent != null) + { + suspendedParent = primaryParent; + suspendedParent.SuspendLayout(); + // Get the parent's grid settings here + GetParentSnapInfo(suspendedParent, behaviorServiceSource); + } + + // If the thing that's being dragged is of 0 size, make the image a little bigger so that the user can see where they're dragging it. + int imageWidth = dragImageRect.Width; + if (imageWidth == 0) + { + imageWidth = 1; + } + + int imageHeight = dragImageRect.Height; + if (imageHeight == 0) + { + imageHeight = 1; + } + + dragImage = new Bitmap(imageWidth, imageHeight, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); + using (Graphics g = Graphics.FromImage(dragImage)) + { + g.Clear(Color.Chartreuse); + } + ((Bitmap)dragImage).MakeTransparent(Color.Chartreuse); + // Gotta use 2 using's here... Too bad. + // Draw each control into the dragimage + using (Graphics g = Graphics.FromImage(dragImage)) + { + using (SolidBrush brush = new SolidBrush(primaryControl.BackColor)) + { + for (int i = 0; i < dragComponents.Length; i++) + { + Rectangle controlRect = new Rectangle(dragComponents[i].draggedLocation.X - dragImageRect.X, + dragComponents[i].draggedLocation.Y - dragImageRect.Y, + dragComponents[i].dragImage.Width, dragComponents[i].dragImage.Height); + // The background + g.FillRectangle(brush, controlRect); + // The foreground + g.DrawImage(dragComponents[i].dragImage, controlRect, + new Rectangle(0, 0, dragComponents[i].dragImage.Width, dragComponents[i].dragImage.Height), + GraphicsUnit.Pixel); + } + } + + } + + originalDragImageLocation = new Point(dragImageRect.X, dragImageRect.Y); + //hide actual controls - this might cause a brief flicker, we are okay with that. + ShowHideDragControls(false); + cleanedUpDrag = false; + } + + internal ArrayList GetSortedDragControls(ref int primaryControlIndex) + { + //create our list of controls-to-drag + ArrayList dragControls = new ArrayList(); + primaryControlIndex = -1; + if ((dragComponents != null) && (dragComponents.Length > 0)) + { + primaryControlIndex = primaryComponentIndex; + for (int i = 0; i < dragComponents.Length; i++) + { + dragControls.Add(dragComponents[i].dragComponent); + } + } + return dragControls; + } + + /// + /// Called by the BehaviorService in response to QueryContinueDrag notifications. + /// + internal void QueryContinueDrag(object sender, QueryContinueDragEventArgs e) + { + //Clean up if the action was cancelled, or we had no effect when dropped. Otherwise EndDragDrop() will do this after the locations have been properly changed. + if (behaviorServiceSource != null && behaviorServiceSource.CancelDrag) + { + e.Action = DragAction.Cancel; + CleanupDrag(true); + return; + } + + if (e.Action == DragAction.Continue) + { + return; + } + + //Clean up if the action was cancelled, or we had no effect when dropped. Otherwise EndDragDrop() will do this after the locations have been properly changed. + if (e.Action == DragAction.Cancel || lastEffect == DragDropEffects.None) + { + CleanupDrag(true); + // QueryContinueDrag can be called before GiveFeedback in which case we will end up here because lastEffect == DragDropEffects.None. If we don't set e.Action, the drag will continue, and GiveFeedback will be called. But since we have cleaned up the drag, weird things happens (e.g. dragImageRegion has been disposed already, so we throw). So if we get here, let's make sure and cancel the drag. + e.Action = DragAction.Cancel; + } + } + + /// + /// Changes the Visible state of the controls we are dragging. Returns whether we change state or not. + /// + internal bool ShowHideDragControls(bool show) + { + if (currentShowState == show) + { + return false; + } + + currentShowState = show; + if (dragComponents != null) + { + for (int i = 0; i < dragComponents.Length; i++) + { + if (dragComponents[i].dragComponent is Control c) + { + c.Visible = show; + } + } + } + return true; + } + + internal void CleanupDrag() + { + CleanupDrag(true); + } + + internal void CleanupDrag(bool clearImages) + { + if (!cleanedUpDrag) + { + if (clearImages) + { + ClearAllDragImages(); + } + + ShowHideDragControls(true); + try + { + if (suspendedParent != null) + { + suspendedParent.ResumeLayout(); + } + } + + finally + { + suspendedParent = null; + //re-enable all glyphs in all adorners + behaviorServiceSource.EnableAllAdorners(true); + if (destHost != srcHost && destHost != null) + { + behaviorServiceTarget.EnableAllAdorners(true); + behaviorServiceTarget.SyncSelection(); + } + + // Layout may have caused controls to resize, which would mean their BodyGlyphs are wrong. We need to sync these. + if (behaviorServiceSource != null) + { + behaviorServiceSource.SyncSelection(); + } + + if (dragImageRegion != null) + { + dragImageRegion.Dispose(); + dragImageRegion = null; + } + + if (dragImage != null) + { + dragImage.Dispose(); + dragImage = null; + } + + if (dragComponents != null) + { + for (int i = 0; i < dragComponents.Length; i++) + { + if (dragComponents[i].dragImage != null) + { + dragComponents[i].dragImage.Dispose(); + dragComponents[i].dragImage = null; + } + } + } + if (graphicsTarget != null) + { + graphicsTarget.Dispose(); + graphicsTarget = null; + } + cleanedUpDrag = true; + } + } + } + + /// + /// This class extends from DataObject and carries additional information such as: the list of Controls currently being dragged and the drag 'Source'. + /// + internal class BehaviorDataObject : DataObject + { + private readonly ICollection _dragComponents; + private readonly Control _source; + private IComponent _target; + private readonly DropSourceBehavior _sourceBehavior; + + public BehaviorDataObject(ICollection dragComponents, Control source, DropSourceBehavior sourceBehavior) : base() + { + _dragComponents = dragComponents; + _source = source; + _sourceBehavior = sourceBehavior; + _target = null; + } + + public Control Source + { + get => _source; + } + + public ICollection DragComponents + { + get => _dragComponents; + } + + public IComponent Target + { + get => _target; + set => _target = value; + } + + internal void EndDragDrop(bool allowSetChildIndexOnDrop) => _sourceBehavior.EndDragDrop(allowSetChildIndexOnDrop); + + internal void CleanupDrag() => _sourceBehavior.CleanupDrag(); + + internal ArrayList GetSortedDragControls(ref int primaryControlIndex) => _sourceBehavior.GetSortedDragControls(ref primaryControlIndex); + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/GrabHandleGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/GrabHandleGlyph.cs new file mode 100644 index 00000000000..888a8b97a4b --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/GrabHandleGlyph.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// The GrabHandleGlyph represents the 8 handles of our new seleciton model. Note that the pen and brush are created once per instance of this class and re-used in our painting logic for perf. reasonse. + /// + internal class GrabHandleGlyph : SelectionGlyphBase + { + private readonly bool _isPrimary = false; + + /// + /// GrabHandleGlyph's constructor takes additional parameters: 'type' and 'primary selection'. Also, we create/cache our pen & brush here to avoid this action with every paint message. + /// + internal GrabHandleGlyph(Rectangle controlBounds, GrabHandleGlyphType type, Behavior behavior, bool primarySelection) : base(behavior) + { + _isPrimary = primarySelection; + hitTestCursor = Cursors.Default; + rules = SelectionRules.None; + + // We +/- DesignerUtils.HANDLEOVERLAP because we want each GrabHandle to overlap the control by DesignerUtils.HANDLEOVERLAP pixels + switch (type) + { + case GrabHandleGlyphType.UpperLeft: + bounds = new Rectangle((controlBounds.X + DesignerUtils.HANDLEOVERLAP) - DesignerUtils.HANDLESIZE, (controlBounds.Y + DesignerUtils.HANDLEOVERLAP) - DesignerUtils.HANDLESIZE, DesignerUtils.HANDLESIZE, DesignerUtils.HANDLESIZE); + hitTestCursor = Cursors.SizeNWSE; + rules = SelectionRules.TopSizeable | SelectionRules.LeftSizeable; + break; + case GrabHandleGlyphType.UpperRight: + bounds = new Rectangle(controlBounds.Right - DesignerUtils.HANDLEOVERLAP, (controlBounds.Y + DesignerUtils.HANDLEOVERLAP) - DesignerUtils.HANDLESIZE, DesignerUtils.HANDLESIZE, DesignerUtils.HANDLESIZE); + hitTestCursor = Cursors.SizeNESW; + rules = SelectionRules.TopSizeable | SelectionRules.RightSizeable; + break; + case GrabHandleGlyphType.LowerRight: + bounds = new Rectangle(controlBounds.Right - DesignerUtils.HANDLEOVERLAP, controlBounds.Bottom - DesignerUtils.HANDLEOVERLAP, DesignerUtils.HANDLESIZE, DesignerUtils.HANDLESIZE); + hitTestCursor = Cursors.SizeNWSE; + rules = SelectionRules.BottomSizeable | SelectionRules.RightSizeable; + break; + case GrabHandleGlyphType.LowerLeft: + bounds = new Rectangle((controlBounds.X + DesignerUtils.HANDLEOVERLAP) - DesignerUtils.HANDLESIZE, controlBounds.Bottom - DesignerUtils.HANDLEOVERLAP, DesignerUtils.HANDLESIZE, DesignerUtils.HANDLESIZE); + hitTestCursor = Cursors.SizeNESW; + rules = SelectionRules.BottomSizeable | SelectionRules.LeftSizeable; + break; + case GrabHandleGlyphType.MiddleTop: + // Only add this one if there's room enough. Room is enough is as follows: 2*HANDLEOVERLAP for UpperLeft and UpperRight handles, 1 HANDLESIZE for the MiddleTop handle, 1 HANDLESIZE for padding + if (controlBounds.Width >= (2 * DesignerUtils.HANDLEOVERLAP) + (2 * DesignerUtils.HANDLESIZE)) + { + bounds = new Rectangle(controlBounds.X + (controlBounds.Width / 2) - (DesignerUtils.HANDLESIZE / 2), (controlBounds.Y + DesignerUtils.HANDLEOVERLAP) - DesignerUtils.HANDLESIZE, DesignerUtils.HANDLESIZE, DesignerUtils.HANDLESIZE); + hitTestCursor = Cursors.SizeNS; + rules = SelectionRules.TopSizeable; + } + break; + case GrabHandleGlyphType.MiddleBottom: + // Only add this one if there's room enough. Room is enough is as follows: 2*HANDLEOVERLAP for LowerLeft and LowerRight handles, 1 HANDLESIZE for the MiddleBottom handle, 1 HANDLESIZE for padding + if (controlBounds.Width >= (2 * DesignerUtils.HANDLEOVERLAP) + (2 * DesignerUtils.HANDLESIZE)) + { + bounds = new Rectangle(controlBounds.X + (controlBounds.Width / 2) - (DesignerUtils.HANDLESIZE / 2), controlBounds.Bottom - DesignerUtils.HANDLEOVERLAP, DesignerUtils.HANDLESIZE, DesignerUtils.HANDLESIZE); + hitTestCursor = Cursors.SizeNS; + rules = SelectionRules.BottomSizeable; + } + break; + case GrabHandleGlyphType.MiddleLeft: + // Only add this one if there's room enough. Room is enough is as follows: 2*HANDLEOVERLAP for UpperLeft and LowerLeft handles, 1 HANDLESIZE for the MiddleLeft handle, 1 HANDLESIZE for padding + if (controlBounds.Height >= (2 * DesignerUtils.HANDLEOVERLAP) + (2 * DesignerUtils.HANDLESIZE)) + { + bounds = new Rectangle((controlBounds.X + DesignerUtils.HANDLEOVERLAP) - DesignerUtils.HANDLESIZE, controlBounds.Y + (controlBounds.Height / 2) - (DesignerUtils.HANDLESIZE / 2), DesignerUtils.HANDLESIZE, DesignerUtils.HANDLESIZE); + hitTestCursor = Cursors.SizeWE; + rules = SelectionRules.LeftSizeable; + } + break; + case GrabHandleGlyphType.MiddleRight: + // Only add this one if there's room enough. Room is enough is as follows: 2*HANDLEOVERLAP for UpperRight and LowerRight handles, 1 HANDLESIZE for the MiddleRight handle, 1 HANDLESIZE for padding + if (controlBounds.Height >= (2 * DesignerUtils.HANDLEOVERLAP) + (2 * DesignerUtils.HANDLESIZE)) + { + bounds = new Rectangle(controlBounds.Right - DesignerUtils.HANDLEOVERLAP, controlBounds.Y + (controlBounds.Height / 2) - (DesignerUtils.HANDLESIZE / 2), DesignerUtils.HANDLESIZE, DesignerUtils.HANDLESIZE); + hitTestCursor = Cursors.SizeWE; + rules = SelectionRules.RightSizeable; + } + break; + default: + Debug.Assert(false, "GrabHandleGlyph was called with a bad GrapHandleGlyphType."); + break; + } + + hitBounds = bounds; + } + + /// + /// Very simple paint logic. + /// + public override void Paint(PaintEventArgs pe) + { + DesignerUtils.DrawGrabHandle(pe.Graphics, bounds, _isPrimary, this); + } + + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/GrabHandleGlyphType.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/GrabHandleGlyphType.cs new file mode 100644 index 00000000000..0999a86dacd --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/GrabHandleGlyphType.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// Describes the type of GrabHandle the GrabHandleGlyph represents. + /// + internal enum GrabHandleGlyphType + { + UpperLeft, + UpperRight, + LowerLeft, + LowerRight, + MiddleTop, + MiddleBottom, + MiddleLeft, + MiddleRight + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/LockedBorderGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/LockedBorderGlyph.cs new file mode 100644 index 00000000000..6f5d06bfc02 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/LockedBorderGlyph.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// The LockedBorderGlyph draws one side (depending on type) of a SelectionBorder in the 'Locked' mode The constructor will initialize and cache the pen and brush objects to avoid uneccessary recreations. + /// + internal class LockedBorderGlyph : SelectionGlyphBase + { + /// + /// This constructor extends from the standard SelectionGlyphBase constructor. Note that a primarySelection flag is passed in - this will be used when determining the colors of the borders. + /// + internal LockedBorderGlyph(Rectangle controlBounds, SelectionBorderGlyphType type) : base(null) + { + InitializeGlyph(controlBounds, type); + } + + /// + /// Helper function that initializes the Glyph based on bounds, type, primary sel, and bordersize. + /// + private void InitializeGlyph(Rectangle controlBounds, SelectionBorderGlyphType type) + { + + hitTestCursor = Cursors.Default; // always default cursor for locked + rules = SelectionRules.None; // never change sel rules for locked + + // this will return the rect representing the bounds of the glyph + bounds = DesignerUtils.GetBoundsForSelectionType(controlBounds, type); + hitBounds = bounds; + } + + /// + /// Simple painting logic for locked Glyphs. + /// + public override void Paint(PaintEventArgs pe) + { + DesignerUtils.DrawSelectionBorder(pe.Graphics, bounds); + } + + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/LockedHandleGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/LockedHandleGlyph.cs new file mode 100644 index 00000000000..c2111519b10 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/LockedHandleGlyph.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// The LockedHandleGlyph represents the handle for a non-resizeable control in our new seleciton model. Note that the pen and brush are created once per instance of this class and re-used in our painting logic for perf. reasonse. + /// + internal class LockedHandleGlyph : SelectionGlyphBase + { + private bool _isPrimary = false; + + /// + /// LockedHandleGlyph's constructor takes additional parameters: 'type' and 'primary selection'. Also, we create/cache our pen & brush here to avoid this action with every paint message. + /// + internal LockedHandleGlyph(Rectangle controlBounds, bool primarySelection) : base(null) + { + _isPrimary = primarySelection; + hitTestCursor = Cursors.Default; + rules = SelectionRules.None; + bounds = new Rectangle((controlBounds.X + DesignerUtils.LOCKHANDLEOVERLAP) - DesignerUtils.LOCKHANDLEWIDTH, + (controlBounds.Y + DesignerUtils.LOCKHANDLEOVERLAP) - DesignerUtils.LOCKHANDLEHEIGHT, + DesignerUtils.LOCKHANDLEWIDTH, DesignerUtils.LOCKHANDLEHEIGHT); + hitBounds = bounds; + } + + /// + /// Very simple paint logic. + /// + public override void Paint(PaintEventArgs pe) + { + DesignerUtils.DrawLockedHandle(pe.Graphics, bounds, _isPrimary, this); + } + + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/MiniLockedBorderGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/MiniLockedBorderGlyph.cs new file mode 100644 index 00000000000..8b0c6d41790 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/MiniLockedBorderGlyph.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// The LockedBorderGlyph draws one side (depending on type) of a SelectionBorder in the 'Locked' mode. The constructor will initialize and cache the pen and brush objects to avoid uneccessary recreations. + /// + internal class MiniLockedBorderGlyph : SelectionGlyphBase + { + private SelectionBorderGlyphType _type; + + internal SelectionBorderGlyphType Type { get => _type; set => _type = value; } + + /// + /// This constructor extends from the standard SelectionGlyphBase constructor. Note that a primarySelection flag is passed in - this will be used when determining the colors of the borders. + /// + internal MiniLockedBorderGlyph(Rectangle controlBounds, SelectionBorderGlyphType type, Behavior behavior, bool primarySelection) : base(behavior) + { + InitializeGlyph(controlBounds, type); + } + + /// + /// Helper function that initializes the Glyph based on bounds, type, primary sel, and bordersize. + /// + private void InitializeGlyph(Rectangle controlBounds, SelectionBorderGlyphType type) + { + hitTestCursor = Cursors.Default; // always default cursor for locked + rules = SelectionRules.None; // never change sel rules for locked + int borderSize = 1; + Type = type; + // this will return the rect representing the bounds of the glyph + bounds = DesignerUtils.GetBoundsForSelectionType(controlBounds, type, borderSize); + hitBounds = bounds; + } + + /// + /// Simple painting logic for locked Glyphs. + /// + public override void Paint(PaintEventArgs pe) + { + pe.Graphics.FillRectangle(new SolidBrush(SystemColors.ControlText), bounds); + } + + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeHandleGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeHandleGlyph.cs new file mode 100644 index 00000000000..4b6c6d9cf24 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeHandleGlyph.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// The NoResizeHandleGlyph represents the handle for a non-resizeable control in our new seleciton model. Note that the pen and brush are created once per instance of this class and re-used in our painting logic for perf. reasonse. + /// + internal class NoResizeHandleGlyph : SelectionGlyphBase + { + private bool _isPrimary = false; + + /// + /// NoResizeHandleGlyph's constructor takes additional parameters: 'type' and 'primary selection'. Also, we create/cache our pen & brush here to avoid this action with every paint message. + /// + internal NoResizeHandleGlyph(Rectangle controlBounds, SelectionRules selRules, bool primarySelection, Behavior behavior) : base(behavior) + { + _isPrimary = primarySelection; + hitTestCursor = Cursors.Default; + rules = SelectionRules.None; + if ((selRules & SelectionRules.Moveable) != 0) + { + rules = SelectionRules.Moveable; + hitTestCursor = Cursors.SizeAll; + } + // The handle is always upperleft + bounds = new Rectangle(controlBounds.X - DesignerUtils.NORESIZEHANDLESIZE, controlBounds.Y - DesignerUtils.NORESIZEHANDLESIZE, DesignerUtils.NORESIZEHANDLESIZE, DesignerUtils.NORESIZEHANDLESIZE); + hitBounds = bounds; + + } + + /// + /// Very simple paint logic. + /// + public override void Paint(PaintEventArgs pe) + { + DesignerUtils.DrawNoResizeHandle(pe.Graphics, bounds, _isPrimary, this); + } + + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeSelectionBorderGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeSelectionBorderGlyph.cs new file mode 100644 index 00000000000..73a26c77587 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeSelectionBorderGlyph.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// The NoResizeSelectionBorderGlyph draws one side (depending on type) of a SelectionBorder. + /// + internal class NoResizeSelectionBorderGlyph : SelectionGlyphBase + { + /// + /// This constructor extends from the standard SelectionGlyphBase constructor. + /// + internal NoResizeSelectionBorderGlyph(Rectangle controlBounds, SelectionRules rules, SelectionBorderGlyphType type, Behavior behavior) : base(behavior) + { + InitializeGlyph(controlBounds, rules, type); + } + + /// + /// Helper function that initializes the Glyph based on bounds, type, and bordersize. + /// + private void InitializeGlyph(Rectangle controlBounds, SelectionRules selRules, SelectionBorderGlyphType type) + { + + rules = SelectionRules.None; + hitTestCursor = Cursors.Default; + if ((selRules & SelectionRules.Moveable) != 0) + { + rules = SelectionRules.Moveable; + hitTestCursor = Cursors.SizeAll; + } + + //this will return the rect representing the bounds of the glyph + bounds = DesignerUtils.GetBoundsForNoResizeSelectionType(controlBounds, type); + hitBounds = bounds; + + // The hitbounds for the border is actually a bit bigger than the glyph bounds + + switch (type) + { + case SelectionBorderGlyphType.Top: + goto case SelectionBorderGlyphType.Bottom; + case SelectionBorderGlyphType.Bottom: + // We want to apply the SELECTIONBORDERHITAREA to the top and the bottom of the selection border glyph + hitBounds.Y -= (DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE) / 2; + hitBounds.Height += DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE; + break; + case SelectionBorderGlyphType.Left: + goto case SelectionBorderGlyphType.Right; + case SelectionBorderGlyphType.Right: + // We want to apply the SELECTIONBORDERHITAREA to the left and the right of the selection border glyph + hitBounds.X -= (DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE) / 2; + hitBounds.Width += DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE; + break; + } + + + + } + + /// + /// Simple painting logic for selection Glyphs. + /// + public override void Paint(PaintEventArgs pe) + { + DesignerUtils.DrawSelectionBorder(pe.Graphics, bounds); + } + + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionBorderGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionBorderGlyph.cs new file mode 100644 index 00000000000..d8a8289e141 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionBorderGlyph.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// The SelectionBorderGlyph draws one side (depending on type) of a SelectionBorder. + /// + internal class SelectionBorderGlyph : SelectionGlyphBase + { + /// + /// This constructor extends from the standard SelectionGlyphBase constructor. + /// + internal SelectionBorderGlyph(Rectangle controlBounds, SelectionRules rules, SelectionBorderGlyphType type, Behavior behavior) : base(behavior) + { + InitializeGlyph(controlBounds, rules, type); + } + + /// + /// Helper function that initializes the Glyph based on bounds, type, and bordersize. + /// + private void InitializeGlyph(Rectangle controlBounds, SelectionRules selRules, SelectionBorderGlyphType type) + { + rules = SelectionRules.None; + hitTestCursor = Cursors.Default; + + //this will return the rect representing the bounds of the glyph + bounds = DesignerUtils.GetBoundsForSelectionType(controlBounds, type); + hitBounds = bounds; + + // The hitbounds for the border is actually a bit bigger than the glyph bounds + switch (type) + { + case SelectionBorderGlyphType.Top: + if ((selRules & SelectionRules.TopSizeable) != 0) + { + hitTestCursor = Cursors.SizeNS; + rules = SelectionRules.TopSizeable; + } + // We want to apply the SELECTIONBORDERHITAREA to the top and the bottom of the selection border glyph + hitBounds.Y -= (DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE) / 2; + hitBounds.Height += DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE; + break; + case SelectionBorderGlyphType.Bottom: + if ((selRules & SelectionRules.BottomSizeable) != 0) + { + hitTestCursor = Cursors.SizeNS; + rules = SelectionRules.BottomSizeable; + } + // We want to apply the SELECTIONBORDERHITAREA to the top and the bottom of the selection border glyph + hitBounds.Y -= (DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE) / 2; + hitBounds.Height += DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE; + break; + case SelectionBorderGlyphType.Left: + if ((selRules & SelectionRules.LeftSizeable) != 0) + { + hitTestCursor = Cursors.SizeWE; + rules = SelectionRules.LeftSizeable; + } + // We want to apply the SELECTIONBORDERHITAREA to the left and the right of the selection border glyph + hitBounds.X -= (DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE) / 2; + hitBounds.Width += DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE; + break; + case SelectionBorderGlyphType.Right: + if ((selRules & SelectionRules.RightSizeable) != 0) + { + hitTestCursor = Cursors.SizeWE; + rules = SelectionRules.RightSizeable; + } + // We want to apply the SELECTIONBORDERHITAREA to the left and the right of the selection border glyph + hitBounds.X -= (DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE) / 2; + hitBounds.Width += DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE; + break; + } + } + + /// + /// Simple painting logic for selection Glyphs. + /// + public override void Paint(PaintEventArgs pe) + { + DesignerUtils.DrawSelectionBorder(pe.Graphics, bounds); + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionBorderGlyphType.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionBorderGlyphType.cs new file mode 100644 index 00000000000..b0936fdd918 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionBorderGlyphType.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// Describes the type of SelectionBorder the SelectionBorderGlyph represents. + /// + internal enum SelectionBorderGlyphType + { + Top, + Bottom, + Left, + Right, + Body + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionGlyphBase.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionGlyphBase.cs new file mode 100644 index 00000000000..3913a68f1fc --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionGlyphBase.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; + +namespace System.Windows.Forms.Design.Behavior +{ + /// + /// This is the base class for all the selection Glyphs: GrabHandle, Hidden, Locked, Selection, and Tray Glyphs. This class includes all like-operations for the Selection glyphs. + /// + internal abstract class SelectionGlyphBase : Glyph + { + protected Rectangle bounds;//defines the bounds of the selection glyph + protected Rectangle hitBounds;//defines the bounds used for hittest - it could be different than the bounds of the glyph itself + protected Cursor hitTestCursor;//the cursor returned if hit test is positive + protected SelectionRules rules;//the selection rules - defining how the control can change + + /// + /// Standard constructor. + /// + internal SelectionGlyphBase(Behavior behavior) : base(behavior) + { + } + + /// + /// Read-only property describing the SelecitonRules for these Glyphs. + /// + public SelectionRules SelectionRules + { + get => rules; + } + + /// + /// Simple hit test rule: if the point is contained within the bounds - then it is a positive hit test. + /// + public override Cursor GetHitTest(Point p) + { + if (hitBounds.Contains(p)) + { + return hitTestCursor; + } + return null; + } + + /// + /// Returns the HitTestCursor for this glyph. + /// + public Cursor HitTestCursor + { + get => hitTestCursor; + } + + /// + /// The Bounds of this glyph. + /// + + public override Rectangle Bounds + { + get => bounds; + } + + /// + /// There's no paint logic on this base class. + /// + public override void Paint(PaintEventArgs pe) + { + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SnapLine.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SnapLine.cs index a9f1d2e944a..5725b6750d8 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SnapLine.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SnapLine.cs @@ -18,6 +18,17 @@ namespace System.Windows.Forms.Design.Behavior /// public sealed class SnapLine { + internal const string Margin = "Margin"; + internal const string MarginRight = Margin + ".Right"; + internal const string MarginLeft = Margin + ".Left"; + internal const string MarginBottom = Margin + ".Bottom"; + internal const string MarginTop = Margin + ".Top"; + internal const string Padding = "Padding"; + internal const string PaddingRight = Padding + ".Right"; + internal const string PaddingLeft = Padding + ".Left"; + internal const string PaddingBottom = Padding + ".Bottom"; + internal const string PaddingTop = Padding + ".Top"; + /// /// SnapLine constructor that takes the type and offset of SnapLine. /// diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ChangeToolStripParentVerb.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ChangeToolStripParentVerb.cs new file mode 100644 index 00000000000..f1e6b3d0857 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ChangeToolStripParentVerb.cs @@ -0,0 +1,156 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing.Design; + +namespace System.Windows.Forms.Design +{ + /// + /// Internal class to provide 'Embed in ToolStripContainer" verb for ToolStrips & MenuStrips. + /// + internal class ChangeToolStripParentVerb + { + private readonly ToolStripDesigner _designer; + private readonly IDesignerHost _host; + private readonly IComponentChangeService _componentChangeSvc; + private readonly IServiceProvider _provider; + + /// + /// Create one of these things... + /// + internal ChangeToolStripParentVerb(string text, ToolStripDesigner designer) + { + Debug.Assert(designer != null, "Can't have a StandardMenuStripVerb without an associated designer"); + _designer = designer; + _provider = designer.Component.Site; + _host = (IDesignerHost)_provider.GetService(typeof(IDesignerHost)); + _componentChangeSvc = (IComponentChangeService)_provider.GetService(typeof(IComponentChangeService)); + } + + /// + /// When the verb is invoked, change the parent of the ToolStrip. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + // This is actually called... + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public void ChangeParent() + { + Cursor current = Cursor.Current; + // create a transaction so this happens as an atomic unit. + DesignerTransaction changeParent = _host.CreateTransaction("Add ToolStripContainer Transaction"); + try + { + Cursor.Current = Cursors.WaitCursor; + //Add a New ToolStripContainer to the RootComponent ... + Control root = _host.RootComponent as Control; + if (_host.GetDesigner(root) is ParentControlDesigner rootDesigner) + { + // close the DAP first - this is so that the autoshown panel on drag drop here is not conflicting with the currently opened panel + // if the verb was called from the panel + ToolStrip toolStrip = _designer.Component as ToolStrip; + if (toolStrip != null && _designer != null && _designer.Component != null && _provider != null) + { + DesignerActionUIService dapuisvc = _provider.GetService(typeof(DesignerActionUIService)) as DesignerActionUIService; + dapuisvc.HideUI(toolStrip); + } + + // Get OleDragHandler ... + ToolboxItem tbi = new ToolboxItem(typeof(System.Windows.Forms.ToolStripContainer)); + OleDragDropHandler ddh = rootDesigner.GetOleDragHandler(); + if (ddh != null) + { + IComponent[] newComp = ddh.CreateTool(tbi, root, 0, 0, 0, 0, false, false); + if (newComp[0] is ToolStripContainer tsc) + { + if (toolStrip != null) + { + IComponentChangeService changeSvc = _provider.GetService(typeof(IComponentChangeService)) as IComponentChangeService; + Control newParent = GetParent(tsc, toolStrip); + PropertyDescriptor controlsProp = TypeDescriptor.GetProperties(newParent)["Controls"]; + Control oldParent = toolStrip.Parent; + if (oldParent != null) + { + changeSvc.OnComponentChanging(oldParent, controlsProp); + //remove control from the old parent + oldParent.Controls.Remove(toolStrip); + } + + if (newParent != null) + { + changeSvc.OnComponentChanging(newParent, controlsProp); + //finally add & relocate the control with the new parent + newParent.Controls.Add(toolStrip); + } + + //fire our comp changed events + if (changeSvc != null && oldParent != null && newParent != null) + { + changeSvc.OnComponentChanged(oldParent, controlsProp, null, null); + changeSvc.OnComponentChanged(newParent, controlsProp, null, null); + } + + //Set the Selection on the new Parent ... so that the selection is restored to the new item, + if (_provider.GetService(typeof(ISelectionService)) is ISelectionService selSvc) + { + selSvc.SetSelectedComponents(new IComponent[] { tsc }); + } + } + } + } + } + } + catch (Exception e) + { + if (e is System.InvalidOperationException) + { + IUIService uiService = (IUIService)_provider.GetService(typeof(IUIService)); + uiService.ShowError(e.Message); + } + + if (changeParent != null) + { + changeParent.Cancel(); + changeParent = null; + } + + } + finally + { + if (changeParent != null) + { + changeParent.Commit(); + changeParent = null; + } + Cursor.Current = current; + } + } + + private Control GetParent(ToolStripContainer container, Control c) + { + Control newParent = container.ContentPanel; + DockStyle dock = c.Dock; + if (c.Parent is ToolStripPanel) + { + dock = c.Parent.Dock; + } + foreach (Control panel in container.Controls) + { + if (panel is ToolStripPanel) + { + if (panel.Dock == dock) + { + newParent = panel; + break; + } + } + } + return newParent; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/CollectionEditVerbManager.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/CollectionEditVerbManager.cs new file mode 100644 index 00000000000..30c3e1a4133 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/CollectionEditVerbManager.cs @@ -0,0 +1,202 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Drawing.Design; + +namespace System.Windows.Forms.Design +{ + /// + /// Class for sharing code for launching the ToolStripItemsCollectionEditor from a verb. This class implments the IWindowsFormsEditorService and ITypeDescriptorContext to display the dialog. + /// + internal class CollectionEditVerbManager : IWindowsFormsEditorService, ITypeDescriptorContext + { + private readonly ComponentDesigner _designer; + private IComponentChangeService _componentChangeSvc; + private readonly PropertyDescriptor _targetProperty; + private readonly DesignerVerb _editItemsVerb; + + /// + /// Create one of these things... + /// + internal CollectionEditVerbManager(string text, ComponentDesigner designer, PropertyDescriptor prop, bool addToDesignerVerbs) + { + Debug.Assert(designer != null, "Can't have a CollectionEditVerbManager without an associated designer"); + _designer = designer; + _targetProperty = prop; + if (prop == null) + { + prop = TypeDescriptor.GetDefaultProperty(designer.Component); + if (prop != null && typeof(ICollection).IsAssignableFrom(prop.PropertyType)) + { + _targetProperty = prop; + } + } + Debug.Assert(_targetProperty != null, "Need PropertyDescriptor for ICollection property to associate collectoin edtior with."); + if (text == null) + { + text = SR.ToolStripItemCollectionEditorVerb; + } + _editItemsVerb = new DesignerVerb(text, new EventHandler(OnEditItems)); + + if (addToDesignerVerbs) + { + _designer.Verbs.Add(_editItemsVerb); + } + } + + /// + /// Our caching property for the IComponentChangeService + /// + private IComponentChangeService ChangeService + { + get + { + if (_componentChangeSvc == null) + { + _componentChangeSvc = (IComponentChangeService)((IServiceProvider)this).GetService(typeof(IComponentChangeService)); + } + return _componentChangeSvc; + } + } + + /// + /// Self-explanitory interface impl. + /// + IContainer ITypeDescriptorContext.Container + { + get + { + if (_designer.Component.Site != null) + { + return _designer.Component.Site.Container; + } + return null; + } + } + + public DesignerVerb EditItemsVerb + { + get => _editItemsVerb; + } + + /// + /// Self-explanitory interface impl. + /// + void ITypeDescriptorContext.OnComponentChanged() => ChangeService.OnComponentChanged(_designer.Component, _targetProperty, null, null); + + /// + /// Self-explanitory interface impl. + /// + bool ITypeDescriptorContext.OnComponentChanging() + { + try + { + ChangeService.OnComponentChanging(_designer.Component, _targetProperty); + } + catch (CheckoutException checkoutException) + { + if (checkoutException == CheckoutException.Canceled) + { + return false; + } + throw; + } + return true; + } + + /// + /// Self-explanitory interface impl. + /// + object ITypeDescriptorContext.Instance + { + get => _designer.Component; + } + + /// + /// Self-explanitory interface impl. + /// + PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor + { + get => _targetProperty; + } + + /// + /// Self-explanitory interface impl. + /// + object IServiceProvider.GetService(Type serviceType) + { + if (serviceType == typeof(ITypeDescriptorContext) || serviceType == typeof(IWindowsFormsEditorService)) + { + return this; + } + if (_designer.Component.Site != null) + { + return _designer.Component.Site.GetService(serviceType); + } + return null; + } + + /// + /// Self-explanitory interface impl. + /// + void IWindowsFormsEditorService.CloseDropDown() + { + // we'll never be called to do this. + Debug.Fail("NOTIMPL"); + return; + } + + /// + /// Self-explanitory interface impl. + /// + void IWindowsFormsEditorService.DropDownControl(Control control) + { + Debug.Fail("NOTIMPL"); + return; + } + + /// + /// Self-explanitory interface impl. + /// + DialogResult IWindowsFormsEditorService.ShowDialog(Form dialog) + { + IUIService uiSvc = (IUIService)((IServiceProvider)this).GetService(typeof(IUIService)); + if (uiSvc != null) + { + return uiSvc.ShowDialog(dialog); + } + else + { + return dialog.ShowDialog(_designer.Component as IWin32Window); + } + } + + /// + /// When the verb is invoked, use all the stuff above to show the dialog, etc. + /// + private void OnEditItems(object sender, EventArgs e) + { + DesignerActionUIService actionUIService = (DesignerActionUIService)((IServiceProvider)this).GetService(typeof(DesignerActionUIService)); + if (actionUIService != null) + { + actionUIService.HideUI(_designer.Component); + } + object propertyValue = _targetProperty.GetValue(_designer.Component); + if (propertyValue == null) + { + return; + } + CollectionEditor itemsEditor = TypeDescriptor.GetEditor(propertyValue, typeof(UITypeEditor)) as CollectionEditor; + Debug.Assert(itemsEditor != null, "Didn't get a collection editor for type '" + _targetProperty.PropertyType.FullName + "'"); + if (itemsEditor != null) + { + itemsEditor.EditValue(this, this, propertyValue); + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/CommandSet.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/CommandSet.cs new file mode 100644 index 00000000000..ba2ee6250ac --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/CommandSet.cs @@ -0,0 +1,3540 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.ComponentModel.Design.Serialization; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Design; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// This class implements the standard set of menu commands for the form designer. This set of command is shared between the form designer (and other UI-based form packages), and composition designer, which doesn't manipulate controls. Therefore, this set of command should only contain commands that are common to both functions. + /// + internal class CommandSet : IDisposable + { + protected ISite site; + private readonly CommandSetItem[] _commandSet; + private IMenuCommandService _menuService; + private IEventHandlerService _eventService; + // Selection service fields. We keep some state about the currently selected components so we can determine proper command enabling quickly. + private ISelectionService _selectionService; + protected int selCount; // the current selection count + protected IComponent primarySelection; // the primary selection, or null + private bool _selectionInherited; // the selection contains inherited components + protected bool controlsOnlySelection; // is the selection containing only controls or are there components in it? + private int _selectionVersion = 1; // the counter of selection changes. + // Selection sort constants + private const int SORT_HORIZONTAL = 0; + private const int SORT_VERTICAL = 1; + private const int SORT_ZORDER = 2; + private const string CF_DESIGNER = "CF_DESIGNERCOMPONENTS_V2"; + //these are used for snapping control via keyboard movement + protected DragAssistanceManager dragManager = null;//point to the snapline engine (only valid between keydown and timer expiration) + private Timer _snapLineTimer;//used to track the time from when a snapline is rendered until it should expire + private BehaviorService _behaviorService;//demand created pointer to the behaviorservice + private StatusCommandUI _statusCommandUI; //Used to update the statusBar Information. + + /// + /// Creates a new CommandSet object. This object implements the set of commands that the UI.Win32 form designer offers. + /// + public CommandSet(ISite site) + { + this.site = site; + _eventService = (IEventHandlerService)site.GetService(typeof(IEventHandlerService)); + Debug.Assert(_eventService != null, "Command set must have the event service. Is command set being initialized too early?"); + _eventService.EventHandlerChanged += new EventHandler(OnEventHandlerChanged); + IDesignerHost host = (IDesignerHost)site.GetService(typeof(IDesignerHost)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + if (host != null) + { + host.Activated += new EventHandler(UpdateClipboardItems); + } + _statusCommandUI = new StatusCommandUI(site); + IUIService uiService = site.GetService(typeof(IUIService)) as IUIService; + // Establish our set of commands + _commandSet = new CommandSetItem[] + { + // Editing commands + new CommandSetItem( this, new EventHandler(OnStatusDelete), new EventHandler(OnMenuDelete), MenuCommands.Delete, uiService), + new CommandSetItem( this, new EventHandler(OnStatusCopy), new EventHandler(OnMenuCopy), MenuCommands.Copy, uiService), + new CommandSetItem( this, new EventHandler(OnStatusCut), new EventHandler(OnMenuCut), MenuCommands.Cut, uiService), + new ImmediateCommandSetItem( this, new EventHandler(OnStatusPaste), new EventHandler(OnMenuPaste), MenuCommands.Paste, uiService), + // Miscellaneous commands + new CommandSetItem( this, new EventHandler(OnStatusSelectAll), new EventHandler(OnMenuSelectAll), MenuCommands.SelectAll, true, uiService), + new CommandSetItem( this, new EventHandler(OnStatusAlways), new EventHandler(OnMenuDesignerProperties), MenuCommands.DesignerProperties, uiService), + // Keyboard commands + new CommandSetItem( this, new EventHandler(OnStatusAlways), new EventHandler(OnKeyCancel), MenuCommands.KeyCancel, uiService), + new CommandSetItem( this, new EventHandler(OnStatusAlways), new EventHandler(OnKeyCancel), MenuCommands.KeyReverseCancel, uiService), + new CommandSetItem( this, new EventHandler(OnStatusPrimarySelection), new EventHandler(OnKeyDefault), MenuCommands.KeyDefaultAction, true, uiService), + new CommandSetItem( this, new EventHandler(OnStatusAnySelection), new EventHandler(OnKeyMove), MenuCommands.KeyMoveUp, true, uiService), + new CommandSetItem( this, new EventHandler(OnStatusAnySelection), new EventHandler(OnKeyMove), MenuCommands.KeyMoveDown, true, uiService), + new CommandSetItem( this, new EventHandler(OnStatusAnySelection), new EventHandler(OnKeyMove), MenuCommands.KeyMoveLeft, true, uiService), + new CommandSetItem( this, new EventHandler(OnStatusAnySelection), new EventHandler(OnKeyMove), MenuCommands.KeyMoveRight, true), + new CommandSetItem(this, new EventHandler(OnStatusAnySelection), new EventHandler(OnKeyMove), MenuCommands.KeyNudgeUp, true, uiService), + new CommandSetItem( this, new EventHandler(OnStatusAnySelection), new EventHandler(OnKeyMove), MenuCommands.KeyNudgeDown, true, uiService), + new CommandSetItem( this, new EventHandler(OnStatusAnySelection), new EventHandler(OnKeyMove), MenuCommands.KeyNudgeLeft, true, uiService), + new CommandSetItem( this, new EventHandler(OnStatusAnySelection), new EventHandler(OnKeyMove), MenuCommands.KeyNudgeRight, true, uiService), + }; + + _selectionService = (ISelectionService)site.GetService(typeof(ISelectionService)); + Debug.Assert(_selectionService != null, "CommandSet relies on the selection service, which is unavailable."); + if (_selectionService != null) + { + _selectionService.SelectionChanged += new EventHandler(OnSelectionChanged); + } + + _menuService = (IMenuCommandService)site.GetService(typeof(IMenuCommandService)); + if (_menuService != null) + { + for (int i = 0; i < _commandSet.Length; i++) + { + _menuService.AddCommand(_commandSet[i]); + } + } + + // Now setup the default command GUID for this designer. This GUID is also used in our toolbar definition file to identify toolbars we own. We store the GUID in a command ID here in the dictionary of the root component. Our host may pull this GUID out and use it. + IDictionaryService ds = site.GetService(typeof(IDictionaryService)) as IDictionaryService; + Debug.Assert(ds != null, "No dictionary service"); + if (ds != null) + { + ds.SetValue(typeof(CommandID), new CommandID(new Guid("BA09E2AF-9DF2-4068-B2F0-4C7E5CC19E2F"), 0)); + } + } + + /// + /// Demand creates a pointer to the BehaviorService + /// + protected BehaviorService BehaviorService + { + get + { + if (_behaviorService == null) + { + _behaviorService = GetService(typeof(BehaviorService)) as BehaviorService; + } + return _behaviorService; + } + } + + /// + /// Retrieves the menu command service, which the command set typically uses quite a bit. + /// + protected IMenuCommandService MenuService + { + get + { + if (_menuService == null) + { + _menuService = (IMenuCommandService)GetService(typeof(IMenuCommandService)); + } + return _menuService; + } + } + + /// + /// Retrieves the selection service, which the command set typically uses quite a bit. + /// + protected ISelectionService SelectionService + { + get => _selectionService; + } + + protected int SelectionVersion + { + get => _selectionVersion; + } + + /// + /// This property demand creates our snaplinetimer used to track how long we'll leave snaplines on the screen before erasing them + /// + protected Timer SnapLineTimer + { + get + { + if (_snapLineTimer == null) + { + //instantiate our snapline timer + _snapLineTimer = new Timer + { + Interval = DesignerUtils.SNAPELINEDELAY + }; + _snapLineTimer.Tick += new EventHandler(OnSnapLineTimerExpire); + } + return _snapLineTimer; + } + } + + /// + /// Checks if an object supports ComponentEditors, and optionally launches the editor. + /// + private bool CheckComponentEditor(object obj, bool launchEditor) + { + if (obj is IComponent) + { + try + { + if (!launchEditor) + { + return true; + } + ComponentEditor editor = (ComponentEditor)TypeDescriptor.GetEditor(obj, typeof(ComponentEditor)); + if (editor == null) + { + return false; + } + + bool success = false; + IComponentChangeService changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (changeService != null) + { + try + { + changeService.OnComponentChanging(obj, null); + } + catch (CheckoutException coEx) + { + if (coEx == CheckoutException.Canceled) + { + return false; + } + throw coEx; + } + catch + { + Debug.Fail("non-CLS compliant exception"); + throw; + } + } + + if (editor is WindowsFormsComponentEditor winEditor) + { + IWin32Window parent = null; + if (obj is IWin32Window) + { +#pragma warning disable 1717 // assignment to self + parent = (IWin32Window)parent; +#pragma warning restore 1717 + } + success = winEditor.EditComponent(obj, parent); + } + else + { + success = editor.EditComponent(obj); + } + + if (success && changeService != null) + { + // Now notify the change service that the change was successful. + changeService.OnComponentChanged(obj, null, null, null); + } + return true; + } + catch (Exception ex) + { + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + } + return false; + } + + /// + /// Disposes of this object, removing all commands from the menu service. + /// + // We don't need to Dispose snapLineTimer + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] + public virtual void Dispose() + { + if (_menuService != null) + { + for (int i = 0; i < _commandSet.Length; i++) + { + _menuService.RemoveCommand(_commandSet[i]); + _commandSet[i].Dispose(); + } + _menuService = null; + } + + if (_selectionService != null) + { + _selectionService.SelectionChanged -= new EventHandler(OnSelectionChanged); + _selectionService = null; + } + + if (_eventService != null) + { + _eventService.EventHandlerChanged -= new EventHandler(OnEventHandlerChanged); + _eventService = null; + } + + IDesignerHost host = (IDesignerHost)site.GetService(typeof(IDesignerHost)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + if (host != null) + { + host.Activated -= new EventHandler(UpdateClipboardItems); + } + + if (_snapLineTimer != null) + { + _snapLineTimer.Stop(); + _snapLineTimer.Tick -= new EventHandler(OnSnapLineTimerExpire); + _snapLineTimer = null; + } + + EndDragManager(); + _statusCommandUI = null; + site = null; + } + + /// + /// Properly cleans up our drag engine. + /// + protected void EndDragManager() + { + if (dragManager != null) + { + if (_snapLineTimer != null) + { + _snapLineTimer.Stop(); + } + dragManager.EraseSnapLines(); + dragManager.OnMouseUp(); + dragManager = null; + } + } + + /// + /// Filters the set of selected components. The selection service will retrieve all components that are currently selected. This method allows you to filter this set down to components that match your criteria. The selectionRules parameter must contain one or more flags from the SelectionRules class. These flags allow you to constrain the set of selected objects to visible, movable, sizeable or all objects. + /// + private object[] FilterSelection(object[] components, SelectionRules selectionRules) + { + object[] selection = null; + if (components == null) + return new object[0]; + // Mask off any selection object that doesn't adhere to the given ruleset. We can ignore this if the ruleset is zero, as all components would be accepted. + if (selectionRules != SelectionRules.None) + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null) + { + ArrayList list = new ArrayList(); + foreach (IComponent comp in components) + { + if (host.GetDesigner(comp) is ControlDesigner des && (des.SelectionRules & selectionRules) == selectionRules) + { + list.Add(comp); + } + } + selection = list.ToArray(); + } + } + return selection ?? (new object[0]); + } + + /// + /// Used to retrieve the selection for a copy. The default implementation retrieves the current selection. + /// + protected virtual ICollection GetCopySelection() + { + ICollection selectedComponents = SelectionService.GetSelectedComponents(); + bool sort = false; + object[] comps = new object[selectedComponents.Count]; + selectedComponents.CopyTo(comps, 0); + foreach (object comp in comps) + { + if (comp is Control) + { + sort = true; + break; + } + } + if (sort) + { + SortSelection(comps, SORT_ZORDER); + } + selectedComponents = comps; + IDesignerHost host = (IDesignerHost)site.GetService(typeof(IDesignerHost)); + if (host != null) + { + ArrayList copySelection = new ArrayList(); + foreach (IComponent comp in selectedComponents) + { + copySelection.Add(comp); + GetAssociatedComponents(comp, host, copySelection); + } + selectedComponents = copySelection; + } + return selectedComponents; + } + + private void GetAssociatedComponents(IComponent component, IDesignerHost host, ArrayList list) + { + if (!(host.GetDesigner(component) is ComponentDesigner designer)) + { + return; + } + foreach (IComponent childComp in designer.AssociatedComponents) + { + if (childComp.Site != null) + { + list.Add(childComp); + GetAssociatedComponents(childComp, host, list); + } + } + } + + /// + /// Used to retrieve the current location of the given component. + /// + private Point GetLocation(IComponent comp) + { + PropertyDescriptor prop = GetProperty(comp, "Location"); + if (prop != null) + { + try + { + return (Point)prop.GetValue(comp); + } + catch (Exception e) + { + Debug.Fail("Commands may be disabled, the location property was not accessible", e.ToString()); + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + } + } + return Point.Empty; + } + + /// + /// Retrieves the given property on the given component. + /// + protected PropertyDescriptor GetProperty(object comp, string propName) + { + return TypeDescriptor.GetProperties(comp)[propName]; + } + + /// + /// Retrieves the requested service. + /// + protected virtual object GetService(Type serviceType) + { + if (site != null) + { + return site.GetService(serviceType); + } + return null; + } + + /// + /// Used to retrieve the current size of the given component. + /// + private Size GetSize(IComponent comp) + { + PropertyDescriptor prop = GetProperty(comp, "Size"); + if (prop != null) + { + return (Size)prop.GetValue(comp); + } + return Size.Empty; + } + + /// + /// Retrieves the snap information for the given component. + /// + protected virtual void GetSnapInformation(IDesignerHost host, IComponent component, out Size snapSize, out IComponent snapComponent, out PropertyDescriptor snapProperty) + { + // This implementation is shared by all. It just looks for snap properties on the base component. + IComponent currentSnapComponent; + PropertyDescriptor gridSizeProp; + PropertyDescriptor currentSnapProp; + PropertyDescriptorCollection props; + + currentSnapComponent = host.RootComponent; + props = TypeDescriptor.GetProperties(currentSnapComponent); + currentSnapProp = props["SnapToGrid"]; + if (currentSnapProp != null && currentSnapProp.PropertyType != typeof(bool)) + { + currentSnapProp = null; + } + gridSizeProp = props["GridSize"]; + if (gridSizeProp != null && gridSizeProp.PropertyType != typeof(Size)) + { + gridSizeProp = null; + } + // Finally, now that we've got the various properties and components, dole out the values. + snapComponent = currentSnapComponent; + snapProperty = currentSnapProp; + if (gridSizeProp != null) + { + snapSize = (Size)gridSizeProp.GetValue(snapComponent); + } + else + { + snapSize = Size.Empty; + } + } + + /// + /// Called before doing any change to multiple controls to check if we have the right to make any change otherwise we would get a checkout message for each control we call setvalue on + /// + protected bool CanCheckout(IComponent comp) + { + // look if it's ok to change + IComponentChangeService changeSvc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + // is it ok to change? + if (changeSvc != null) + { + try + { + changeSvc.OnComponentChanging(comp, null); + } + catch (CheckoutException chkex) + { + if (chkex == CheckoutException.Canceled) + return false; + throw chkex; + } + } + return true; + } + + /// + /// Called by the event handler service when the current event handler has changed. Here we invalidate all of our menu items so that they can pick up the new event handler. + /// + private void OnEventHandlerChanged(object sender, EventArgs e) + { + OnUpdateCommandStatus(); + } + + /// + /// Called for the two cancel commands we support. + /// + private void OnKeyCancel(object sender, EventArgs e) + { + OnKeyCancel(sender); + } + + /// + /// Called for the two cancel commands we support. Returns true If we did anything with the cancel, or false if not. + /// + protected virtual bool OnKeyCancel(object sender) + { + bool handled = false; + // The base implementation here just checks to see if we are dragging. If we are, then we abort the drag. + if (BehaviorService != null && BehaviorService.HasCapture) + { + BehaviorService.OnLoseCapture(); + handled = true; + } + else + { + IToolboxService tbx = (IToolboxService)GetService(typeof(IToolboxService)); + if (tbx != null && tbx.GetSelectedToolboxItem((IDesignerHost)GetService(typeof(IDesignerHost))) != null) + { + tbx.SelectedToolboxItemUsed(); + NativeMethods.POINT p = new NativeMethods.POINT(); + NativeMethods.GetCursorPos(p); + IntPtr hwnd = NativeMethods.WindowFromPoint(p.x, p.y); + if (hwnd != IntPtr.Zero) + { + NativeMethods.SendMessage(hwnd, NativeMethods.WM_SETCURSOR, hwnd, (IntPtr)NativeMethods.HTCLIENT); + } + else + { + Cursor.Current = Cursors.Default; + } + handled = true; + } + } + return handled; + } + + /// + /// Called for the "default" command, typically the Enter key. + /// + protected void OnKeyDefault(object sender, EventArgs e) + { + // Return key. Handle it like a double-click on the primary selection + ISelectionService selSvc = SelectionService; + if (selSvc != null) + { + if (selSvc.PrimarySelection is IComponent pri) + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null) + { + IDesigner designer = host.GetDesigner(pri); + + if (designer != null) + { + designer.DoDefaultAction(); + } + } + } + } + } + + /// + /// Called for all cursor movement commands. + /// + protected virtual void OnKeyMove(object sender, EventArgs e) + { + // Arrow keys. Begin a drag if the selection isn't locked. + ISelectionService selSvc = SelectionService; + if (selSvc != null) + { + if (selSvc.PrimarySelection is IComponent comp) + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null) + { + PropertyDescriptor lockedProp = TypeDescriptor.GetProperties(comp)["Locked"]; + if (lockedProp == null || (lockedProp.PropertyType == typeof(bool) && ((bool)lockedProp.GetValue(comp))) == false) + { + CommandID cmd = ((MenuCommand)sender).CommandID; + bool invertSnap = false; + int moveOffsetX = 0; + int moveOffsetY = 0; + + if (cmd.Equals(MenuCommands.KeyMoveUp)) + { + moveOffsetY = -1; + } + else if (cmd.Equals(MenuCommands.KeyMoveDown)) + { + moveOffsetY = 1; + } + else if (cmd.Equals(MenuCommands.KeyMoveLeft)) + { + moveOffsetX = -1; + } + else if (cmd.Equals(MenuCommands.KeyMoveRight)) + { + moveOffsetX = 1; + } + else if (cmd.Equals(MenuCommands.KeyNudgeUp)) + { + moveOffsetY = -1; + invertSnap = true; + } + else if (cmd.Equals(MenuCommands.KeyNudgeDown)) + { + moveOffsetY = 1; + invertSnap = true; + } + else if (cmd.Equals(MenuCommands.KeyNudgeLeft)) + { + moveOffsetX = -1; + invertSnap = true; + } + else if (cmd.Equals(MenuCommands.KeyNudgeRight)) + { + moveOffsetX = 1; + invertSnap = true; + } + else + { + Debug.Fail("Unknown command mapped to OnKeyMove: " + cmd.ToString()); + } + + DesignerTransaction trans; + if (selSvc.SelectionCount > 1) + { + trans = host.CreateTransaction(string.Format(SR.DragDropMoveComponents, selSvc.SelectionCount)); + } + else + { + trans = host.CreateTransaction(string.Format(SR.DragDropMoveComponent, comp.Site.Name)); + } + try + { + //if we can find the behaviorservice, then we can use it and the SnapLineEngine to help us move these controls... + if (BehaviorService != null) + { + Control primaryControl = comp as Control; //this can be null (when we are moving a component in the ComponenTray) + bool useSnapLines = BehaviorService.UseSnapLines; + // If we have previous snaplines, we always want to erase them, no matter what. VS Whidbey #397709 + if (dragManager != null) + { + EndDragManager(); + } + + //If we CTRL+Arrow and we're using SnapLines - snap to the next location. Don't snap if we are moving a component in the ComponentTray + if (invertSnap && useSnapLines && primaryControl != null) + { + ArrayList selComps = new ArrayList(selSvc.GetSelectedComponents()); + //create our snapline engine + dragManager = new DragAssistanceManager(comp.Site, selComps); + //ask our snapline engine to find the nearest snap position with the given direction + Point snappedOffset = dragManager.OffsetToNearestSnapLocation(primaryControl, new Point(moveOffsetX, moveOffsetY)); + //update the offset according to the snapline engine + // This is the offset assuming origin is in the upper-left. + moveOffsetX = snappedOffset.X; + moveOffsetY = snappedOffset.Y; + // If the parent is mirrored then we need to negate moveOffsetX. This is because moveOffsetX assumes that the origin is upper left. That is, when moveOffsetX is positive, we are moving right, negative when moving left. The parent container's origin depends on its mirroring property. Thus when we call propLoc.setValue below, we need to make sure that our moveOffset.X correctly reflects the placement of the parent container's origin. We need to do this AFTER we calculate the snappedOffset. This is because the dragManager calculations are all based on an origin in the upper-left. + if (primaryControl.Parent.IsMirrored) + { + moveOffsetX *= -1; + } + } + //if we used a regular arrow key and we're in SnapToGrid mode... + else if (!invertSnap && !useSnapLines) + { + bool snapOn = false; + Size snapSize = Size.Empty; + GetSnapInformation(host, comp, out snapSize, out IComponent snapComponent, out PropertyDescriptor snapProperty); + if (snapProperty != null) + { + snapOn = (bool)snapProperty.GetValue(snapComponent); + } + if (snapOn && !snapSize.IsEmpty) + { + moveOffsetX *= snapSize.Width; + moveOffsetY *= snapSize.Height; + if (primaryControl != null) + { + //ask the parent to adjust our wanna-be snapped position + if (host.GetDesigner(primaryControl.Parent) is ParentControlDesigner parentDesigner) + { + Point loc = primaryControl.Location; + // If the parent is mirrored then we need to negate moveOffsetX. This is because moveOffsetX assumes that the origin is upper left. That is, when moveOffsetX is positive, we are moving right, negative when moving left. The parent container's origin depends on its mirroring property. Thus when we call propLoc.setValue below, we need to make sure that our moveOffset.X correctly reflects the placement of the parent container's origin. Should do this BEFORE we get the snapped point. + if (primaryControl.Parent.IsMirrored) + { + moveOffsetX *= -1; + } + loc.Offset(moveOffsetX, moveOffsetY); + loc = parentDesigner.GetSnappedPoint(loc); + //reset our offsets now that we've snapped correctly + if (moveOffsetX != 0) + { + moveOffsetX = loc.X - primaryControl.Location.X; + } + if (moveOffsetY != 0) + { + moveOffsetY = loc.Y - primaryControl.Location.Y; + } + } + } + } + else + { + // In this case we are just going to move 1 pixel, so let's adjust for Mirroring + if (primaryControl != null && primaryControl.Parent.IsMirrored) + { + moveOffsetX *= -1; + } + } + } + else + { + if (primaryControl != null && primaryControl.Parent.IsMirrored) + { + moveOffsetX *= -1; + } + } + + SelectionRules rules = SelectionRules.Moveable | SelectionRules.Visible; + foreach (IComponent component in selSvc.GetSelectedComponents()) + { + if (host.GetDesigner(component) is ControlDesigner des && ((des.SelectionRules & rules) != rules)) + { + //the control must match the rules, if not, then we don't move it + continue; + } + // Components are always moveable and visible + PropertyDescriptor propLoc = TypeDescriptor.GetProperties(component)["Location"]; + if (propLoc != null) + { + Point loc = (Point)propLoc.GetValue(component); + loc.Offset(moveOffsetX, moveOffsetY); + propLoc.SetValue(component, loc); + } + //change the Status information .... + if (component == selSvc.PrimarySelection && _statusCommandUI != null) + { + _statusCommandUI.SetStatusInformation(component as Component); + } + } + } + } + finally + { + if (trans != null) + { + trans.Commit(); + } + + if (dragManager != null) + { + //start our timer for the snaplines + SnapLineTimer.Start(); + + //render any lines + dragManager.RenderSnapLinesInternal(); + } + } + } + } + } + } + } + + /// + /// Called for all alignment operations that key off of a primary selection. + /// + protected void OnMenuAlignByPrimary(object sender, EventArgs e) + { + MenuCommand cmd = (MenuCommand)sender; + CommandID id = cmd.CommandID; + + //Need to get the location for the primary control, we do this here (instead of onselectionchange) because the control could be dragged around once it is selected and might have a new location + Point primaryLocation = GetLocation(primarySelection); + Size primarySize = GetSize(primarySelection); + if (SelectionService == null) + { + return; + } + + Cursor oldCursor = Cursor.Current; + try + { + Cursor.Current = Cursors.WaitCursor; + // Now loop through each of the components. + ICollection comps = SelectionService.GetSelectedComponents(); + // Inform the designer that we are about to monkey with a ton of properties. + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + DesignerTransaction trans = null; + try + { + if (host != null) + { + trans = host.CreateTransaction(string.Format(SR.CommandSetAlignByPrimary, comps.Count)); + } + + bool firstTry = true; + Point loc = Point.Empty; + foreach (object obj in comps) + { + if (obj == primarySelection) + { + continue; + } + IComponent comp = obj as IComponent; + if (comp != null && host != null) + { + if (!(host.GetDesigner(comp) is ControlDesigner des)) + { + continue; + } + } + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(comp); + PropertyDescriptor locProp = props["Location"]; + PropertyDescriptor sizeProp = props["Size"]; + PropertyDescriptor lockProp = props["Locked"]; + // Skip all components that are locked + // + if (lockProp != null) + { + if ((bool)lockProp.GetValue(comp)) + continue; + } + + // Skip all components that don't have a location property + // + if (locProp == null || locProp.IsReadOnly) + { + continue; + } + + // Skip all components that don't have size if we're doing a size operation. + if (id.Equals(MenuCommands.AlignBottom) || + id.Equals(MenuCommands.AlignHorizontalCenters) || + id.Equals(MenuCommands.AlignVerticalCenters) || + id.Equals(MenuCommands.AlignRight)) + { + if (sizeProp == null || sizeProp.IsReadOnly) + { + continue; + } + } + + // Align bottom + if (id.Equals(MenuCommands.AlignBottom)) + { + loc = (Point)locProp.GetValue(comp); + Size size = (Size)sizeProp.GetValue(comp); + loc.Y = primaryLocation.Y + primarySize.Height - size.Height; + } + // Align horizontal centers + else if (id.Equals(MenuCommands.AlignHorizontalCenters)) + { + loc = (Point)locProp.GetValue(comp); + Size size = (Size)sizeProp.GetValue(comp); + loc.Y = primarySize.Height / 2 + primaryLocation.Y - size.Height / 2; + } + // Align left + else if (id.Equals(MenuCommands.AlignLeft)) + { + loc = (Point)locProp.GetValue(comp); + loc.X = primaryLocation.X; + } + // Align right + else if (id.Equals(MenuCommands.AlignRight)) + { + loc = (Point)locProp.GetValue(comp); + Size size = (Size)sizeProp.GetValue(comp); + loc.X = primaryLocation.X + primarySize.Width - size.Width; + } + // Align top + else if (id.Equals(MenuCommands.AlignTop)) + { + loc = (Point)locProp.GetValue(comp); + loc.Y = primaryLocation.Y; + } + // Align vertical centers + else if (id.Equals(MenuCommands.AlignVerticalCenters)) + { + loc = (Point)locProp.GetValue(comp); + Size size = (Size)sizeProp.GetValue(comp); + loc.X = primarySize.Width / 2 + primaryLocation.X - size.Width / 2; + } + else + { + Debug.Fail("Unrecognized command: " + id.ToString()); + } + + if (firstTry && !CanCheckout(comp)) + { + return; + } + firstTry = false; + locProp.SetValue(comp, loc); + } + } + finally + { + if (trans != null) + { + trans.Commit(); + } + } + } + finally + { + Cursor.Current = oldCursor; + } + } + + /// + /// Called when the align->to grid menu item is selected. + /// + protected void OnMenuAlignToGrid(object sender, EventArgs e) + { + Size gridSize = Size.Empty; + if (SelectionService == null) + { + return; + } + + Cursor oldCursor = Cursor.Current; + try + { + Cursor.Current = Cursors.WaitCursor; + ICollection selectedComponents = SelectionService.GetSelectedComponents(); + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + DesignerTransaction trans = null; + try + { + if (host != null) + { + trans = host.CreateTransaction(string.Format(SR.CommandSetAlignToGrid, selectedComponents.Count)); + if (host.RootComponent is Control baseComponent) + { + PropertyDescriptor prop = GetProperty(baseComponent, "GridSize"); + if (prop != null) + { + gridSize = (Size)prop.GetValue(baseComponent); + } + if (prop == null || gridSize.IsEmpty) + { + //bail silently here + return; + } + } + + } + bool firstTry = true; + // for each component, we round to the nearest snap offset for x and y + foreach (object comp in selectedComponents) + { + // first check to see if the component is locked, if so - don't move it... + PropertyDescriptor lockedProp = GetProperty(comp, "Locked"); + if (lockedProp != null && ((bool)lockedProp.GetValue(comp)) == true) + { + continue; + } + // if the designer for this component isn't a ControlDesigner (maybe it's something in the component tray) then don't try to align it to grid. + IComponent component = comp as IComponent; + if (component != null && host != null) + { + if (!(host.GetDesigner(component) is ControlDesigner des)) + { + continue; + } + } + + // get the location property + PropertyDescriptor locProp = GetProperty(comp, "Location"); + + // get the current value + if (locProp == null || locProp.IsReadOnly) + { + continue; + } + Point loc = (Point)locProp.GetValue(comp); + + // round the x to the snap size + int delta = loc.X % gridSize.Width; + if (delta < (gridSize.Width / 2)) + { + loc.X -= delta; + } + else + { + loc.X += (gridSize.Width - delta); + } + + // round the y to the gridsize + delta = loc.Y % gridSize.Height; + if (delta < (gridSize.Height / 2)) + { + loc.Y -= delta; + } + else + { + loc.Y += (gridSize.Height - delta); + } + + // look if it's ok to change + if (firstTry && !CanCheckout(component)) + { + return; + } + firstTry = false; + + // set the value + locProp.SetValue(comp, loc); + } + } + finally + { + if (trans != null) + { + trans.Commit(); + } + } + } + finally + { + Cursor.Current = oldCursor; + } + } + + /// + /// Called when the center horizontally or center vertically menu item is selected. + /// + protected void OnMenuCenterSelection(object sender, EventArgs e) + { + MenuCommand cmd = (MenuCommand)sender; + CommandID cmdID = cmd.CommandID; + if (SelectionService == null) + { + return; + } + + Cursor oldCursor = Cursor.Current; + try + { + Cursor.Current = Cursors.WaitCursor; + // NOTE: this only works on Control types + ICollection selectedComponents = SelectionService.GetSelectedComponents(); + Control viewParent = null; + Size size = Size.Empty; + Point loc = Point.Empty; + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + DesignerTransaction trans = null; + try + { + if (host != null) + { + string batchString; + + if (cmdID == MenuCommands.CenterHorizontally) + { + batchString = string.Format(SR.WindowsFormsCommandCenterX, selectedComponents.Count); + } + else + { + batchString = string.Format(SR.WindowsFormsCommandCenterY, selectedComponents.Count); + } + trans = host.CreateTransaction(batchString); + } + //subhag calculate the union REctangle + int top = int.MaxValue; + int left = int.MaxValue; + int right = int.MinValue; + int bottom = int.MinValue; + + foreach (object obj in selectedComponents) + { + if (obj is Control) + { + IComponent comp = (IComponent)obj; + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(comp); + PropertyDescriptor locProp = props["Location"]; + PropertyDescriptor sizeProp = props["Size"]; + // Skip all components that don't have location and size properties + if (locProp == null || sizeProp == null || locProp.IsReadOnly || sizeProp.IsReadOnly) + { + continue; + } + + // Also, skip all locked componenents... + PropertyDescriptor lockProp = props["Locked"]; + if (lockProp != null && (bool)lockProp.GetValue(comp) == true) + { + continue; + } + + size = (Size)sizeProp.GetValue(comp); + loc = (Point)locProp.GetValue(comp); + + //cache the first parent we see - if there's a mix of different parents - we'll just center based on the first one + if (viewParent == null) + { + viewParent = ((Control)comp).Parent; + } + + if (loc.X < left) + left = loc.X; + if (loc.Y < top) + top = loc.Y; + if (loc.X + size.Width > right) + right = loc.X + size.Width; + if (loc.Y + size.Height > bottom) + bottom = loc.Y + size.Height; + } + } + + //if we never found a viewParent (some read-only inherited scenarios then simply bail + if (viewParent == null) + { + return; + } + + int centerOfUnionRectX = (left + right) / 2; + int centerOfUnionRectY = (top + bottom) / 2; + int centerOfParentX = (viewParent.ClientSize.Width) / 2; + int centerOfParentY = (viewParent.ClientSize.Height) / 2; + int deltaX = 0; + int deltaY = 0; + bool shiftRight = false; + bool shiftBottom = false; + if (centerOfParentX >= centerOfUnionRectX) + { + deltaX = centerOfParentX - centerOfUnionRectX; + shiftRight = true; + } + else + deltaX = centerOfUnionRectX - centerOfParentX; + + if (centerOfParentY >= centerOfUnionRectY) + { + deltaY = centerOfParentY - centerOfUnionRectY; + shiftBottom = true; + } + else + deltaY = centerOfUnionRectY - centerOfParentY; + + bool firstTry = true; + foreach (object obj in selectedComponents) + { + if (obj is Control) + { + IComponent comp = (IComponent)obj; + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(comp); + PropertyDescriptor locProp = props["Location"]; + if (locProp.IsReadOnly) + { + continue; + } + + loc = (Point)locProp.GetValue(comp); + + if (cmdID == MenuCommands.CenterHorizontally) + { + if (shiftRight) + loc.X += deltaX; + else + loc.X -= deltaX; + } + else if (cmdID == MenuCommands.CenterVertically) + { + if (shiftBottom) + loc.Y += deltaY; + else + loc.Y -= deltaY; + } + // look if it's ok to change the first time + if (firstTry && !CanCheckout(comp)) + { + return; + } + firstTry = false; + // do the change + locProp.SetValue(comp, loc); + } + } + } + finally + { + if (trans != null) + { + trans.Commit(); + } + } + } + finally + { + Cursor.Current = oldCursor; + } + } + + /// + /// Called when the copy menu item is selected. + /// + protected void OnMenuCopy(object sender, EventArgs e) + { + if (SelectionService == null) + { + return; + } + + Cursor oldCursor = Cursor.Current; + try + { + Cursor.Current = Cursors.WaitCursor; + ICollection selectedComponents = GetCopySelection(); + selectedComponents = PrependComponentNames(selectedComponents); + IDesignerSerializationService ds = (IDesignerSerializationService)GetService(typeof(IDesignerSerializationService)); + Debug.Assert(ds != null, "No designer serialization service -- we cannot copy to clipboard"); + if (ds != null) + { + object serializationData = ds.Serialize(selectedComponents); + MemoryStream stream = new MemoryStream(); + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(stream, serializationData); + stream.Seek(0, SeekOrigin.Begin); + byte[] bytes = stream.GetBuffer(); + IDataObject dataObj = new DataObject(CF_DESIGNER, bytes); + Clipboard.SetDataObject(dataObj); + } + UpdateClipboardItems(null, null); + } + finally + { + Cursor.Current = oldCursor; + } + } + + /// + /// Called when the cut menu item is selected. + /// + protected void OnMenuCut(object sender, EventArgs e) + { + if (SelectionService == null) + { + return; + } + Cursor oldCursor = Cursor.Current; + try + { + Cursor.Current = Cursors.WaitCursor; + ICollection selectedComponents = GetCopySelection(); + int cutCount = selectedComponents.Count; + selectedComponents = PrependComponentNames(selectedComponents); + IDesignerSerializationService ds = (IDesignerSerializationService)GetService(typeof(IDesignerSerializationService)); + Debug.Assert(ds != null, "No designer serialization service -- we cannot copy to clipboard"); + if (ds != null) + { + object serializationData = ds.Serialize(selectedComponents); + MemoryStream stream = new MemoryStream(); + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(stream, serializationData); + stream.Seek(0, SeekOrigin.Begin); + byte[] bytes = stream.GetBuffer(); + IDataObject dataObj = new DataObject(CF_DESIGNER, bytes); + Clipboard.SetDataObject(dataObj); + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Control commonParent = null; + if (host != null) + { + IComponentChangeService changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + DesignerTransaction trans = null; + + ArrayList designerList = new ArrayList(); + try + { + trans = host.CreateTransaction(string.Format(SR.CommandSetCutMultiple, cutCount)); + // clear the selected components so we aren't browsing them + SelectionService.SetSelectedComponents(new object[0], SelectionTypes.Replace); + object[] selComps = new object[selectedComponents.Count]; + selectedComponents.CopyTo(selComps, 0); + for (int i = 0; i < selComps.Length; i++) + { + object obj = selComps[i]; + // We should never delete the base component. + // + if (obj == host.RootComponent || !(obj is IComponent component)) + { + continue; + } + //Perf: We suspend Component Changing Events on parent for bulk changes to avoid unnecessary serialization\deserialization for undo + if (obj is Control c) + { + Control parent = c.Parent; + if (parent != null) + { + if (host.GetDesigner(parent) is ParentControlDesigner designer && !designerList.Contains(designer)) + { + designer.SuspendChangingEvents(); + designerList.Add(designer); + designer.ForceComponentChanging(); + } + } + } + } + // go backward so we destroy parents before children + + for (int i = 0; i < selComps.Length; i++) + { + object obj = selComps[i]; + // We should never delete the base component. + // + if (obj == host.RootComponent || !(obj is IComponent component)) + { + continue; + } + + Control c = obj as Control; + //Cannot use idx = 1 to check (see diff) due to the call to PrependComponentNames, which adds non IComponent objects to the beginning of selectedComponents. Thus when we finally get here idx would be > 1. + if (commonParent == null && c != null) + { + commonParent = c.Parent; + } + else if (commonParent != null && c != null) + { + Control selectedControl = c; + + if (selectedControl.Parent != commonParent && !commonParent.Contains(selectedControl)) + { + // look for internal parenting + if (selectedControl == commonParent || selectedControl.Contains(commonParent)) + { + commonParent = selectedControl.Parent; + } + else + { + commonParent = null; + } + } + } + if (component != null) + { + ArrayList al = new ArrayList(); + GetAssociatedComponents(component, host, al); + foreach (IComponent comp in al) + { + changeService.OnComponentChanging(comp, null); + } + + host.DestroyComponent(component); + } + } + } + finally + { + if (trans != null) + trans.Commit(); + foreach (ParentControlDesigner des in designerList) + { + if (des != null) + { + des.ResumeChangingEvents(); + } + } + } + if (commonParent != null) + { + SelectionService.SetSelectedComponents(new object[] { commonParent }, SelectionTypes.Replace); + } + else if (SelectionService.PrimarySelection == null) + { + SelectionService.SetSelectedComponents(new object[] { host.RootComponent }, SelectionTypes.Replace); + } + } + } + } + finally + { + Cursor.Current = oldCursor; + } + } + + /// + /// Called when the delete menu item is selected. + /// + protected void OnMenuDelete(object sender, EventArgs e) + { + Cursor oldCursor = Cursor.Current; + try + { + Cursor.Current = Cursors.WaitCursor; + if (site != null) + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + if (SelectionService == null) + { + return; + } + + if (host != null) + { + IComponentChangeService changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + ICollection comps = SelectionService.GetSelectedComponents(); + string desc = string.Format(SR.CommandSetDelete, comps.Count); + DesignerTransaction trans = null; + IComponent commonParent = null; + bool commonParentSet = false; + ArrayList designerList = new ArrayList(); + try + { + trans = host.CreateTransaction(desc); + SelectionService.SetSelectedComponents(new object[0], SelectionTypes.Replace); + foreach (object obj in comps) + { + if (!(obj is IComponent comp) || comp.Site == null) + { + continue; + } + //Perf: We suspend Component Changing Events on parent for bulk changes to avoid unnecessary serialization\deserialization for undo + if (obj is Control c) + { + Control parent = c.Parent; + if (parent != null) + { + if (host.GetDesigner(parent) is ParentControlDesigner designer && !designerList.Contains(designer)) + { + designer.SuspendChangingEvents(); + designerList.Add(designer); + designer.ForceComponentChanging(); + } + } + } + } + foreach (object obj in comps) + { + // If it's not a component, we can't delete it. It also may have already been deleted as part of a parent operation, so we skip it. + if (!(obj is IComponent c) || c.Site == null) + { + continue; + } + // We should never delete the base component. + if (obj == host.RootComponent) + { + continue; + } + Control control = obj as Control; + if (!commonParentSet) + { + if (control != null) + { + commonParent = control.Parent; + } + else + { + // if this is not a Control, see if we can get an ITreeDesigner from it, and figure out the Component from that. + if (host.GetDesigner((IComponent)obj) is ITreeDesigner designer) + { + IDesigner parentDesigner = designer.Parent; + if (parentDesigner != null) + { + commonParent = parentDesigner.Component; + } + } + } + commonParentSet = (commonParent != null); + } + else if (commonParent != null) + { + if (control != null && commonParent is Control) + { + Control selectedControl = control; + Control controlCommonParent = (Control)commonParent; + if (selectedControl.Parent != controlCommonParent && !controlCommonParent.Contains(selectedControl)) + { + // look for internal parenting + if (selectedControl == controlCommonParent || selectedControl.Contains(controlCommonParent)) + { + commonParent = selectedControl.Parent; + } + else + { + // start walking up until we find a common parent + while (controlCommonParent != null && !controlCommonParent.Contains(selectedControl)) + { + controlCommonParent = controlCommonParent.Parent; + } + commonParent = controlCommonParent; + } + } + } + else + { + // for these we aren't as thorough as we are with the Control-based ones. we just walk up the chain until we find that parent or the root component. + if (host.GetDesigner((IComponent)obj) is ITreeDesigner designer && host.GetDesigner(commonParent) is ITreeDesigner commonParentDesigner && designer.Parent != commonParentDesigner) + { + ArrayList designerChain = new ArrayList(); + ArrayList parentDesignerChain = new ArrayList(); + // walk the chain of designers from the current parent designer up to the root component, and for the current component designer. + for (designer = designer.Parent as ITreeDesigner; + designer != null; + designer = designer.Parent as ITreeDesigner) + { + designerChain.Add(designer); + } + + for (commonParentDesigner = commonParentDesigner.Parent as ITreeDesigner; commonParentDesigner != null; commonParentDesigner = commonParentDesigner.Parent as ITreeDesigner) + { + parentDesignerChain.Add(commonParentDesigner); + } + + // now that we've got the trees built up, start comparing them from the ends to see where they diverge. + ArrayList shorterList = designerChain.Count < parentDesignerChain.Count ? designerChain : parentDesignerChain; + ArrayList longerList = (shorterList == designerChain ? parentDesignerChain : designerChain); + commonParentDesigner = null; + if (shorterList.Count > 0 && longerList.Count > 0) + { + int shortIndex = Math.Max(0, shorterList.Count - 1); + int longIndex = Math.Max(0, longerList.Count - 1); + while (shortIndex >= 0 && longIndex >= 0) + { + if (shorterList[shortIndex] != longerList[longIndex]) + { + break; + } + commonParentDesigner = (ITreeDesigner)shorterList[shortIndex]; + shortIndex--; + longIndex--; + } + } + // alright, what have we got? + if (commonParentDesigner != null) + { + commonParent = commonParentDesigner.Component; + } + else + { + commonParent = null; + } + } + } + } + ArrayList al = new ArrayList(); + GetAssociatedComponents((IComponent)obj, host, al); + foreach (IComponent comp in al) + { + changeService.OnComponentChanging(comp, null); + } + host.DestroyComponent((IComponent)obj); + } + } + finally + { + if (trans != null) + { + trans.Commit(); + } + + foreach (ParentControlDesigner des in designerList) + { + if (des != null) + { + des.ResumeChangingEvents(); + } + } + } + + if (commonParent != null && SelectionService.PrimarySelection == null) + { + if (host.GetDesigner(commonParent) is ITreeDesigner commonParentDesigner && commonParentDesigner.Children != null) + { + // choose the first child of the common parent if it has any. + foreach (IDesigner designer in commonParentDesigner.Children) + { + IComponent component = designer.Component; + if (component.Site != null) + { + commonParent = component; + break; + } + } + } + else if (commonParent is Control controlCommonParent) + { + // if we have a common parent, select it's first child + if (controlCommonParent.Controls.Count > 0) + { + controlCommonParent = controlCommonParent.Controls[0]; + while (controlCommonParent != null && controlCommonParent.Site == null) + { + controlCommonParent = controlCommonParent.Parent; + } + commonParent = controlCommonParent; + } + } + if (commonParent != null) + { + SelectionService.SetSelectedComponents(new object[] { commonParent }, SelectionTypes.Replace); + } + else + { + SelectionService.SetSelectedComponents(new object[] { host.RootComponent }, SelectionTypes.Replace); + } + } + else + { + if (SelectionService.PrimarySelection == null) + { + SelectionService.SetSelectedComponents(new object[] { host.RootComponent }, SelectionTypes.Replace); + } + } + } + } + } + finally + { + Cursor.Current = oldCursor; + } + } + + /// + /// Called when the paste menu item is selected. + /// + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1809:AvoidExcessiveLocals")] + protected void OnMenuPaste(object sender, EventArgs e) + { + Cursor oldCursor = Cursor.Current; + ArrayList designerList = new ArrayList(); + try + { + Cursor.Current = Cursors.WaitCursor; + // If a control fails to get pasted; then we should remember its associatedComponents so that they are not pasted. + ICollection associatedCompsOfFailedContol = null; + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + if (host == null) + return; // nothing we can do here! + IDataObject dataObj = Clipboard.GetDataObject(); + ICollection components = null; + bool createdItems = false; + ComponentTray tray = null; + int numberOfOriginalTrayControls = 0; + // Get the current number of controls in the Component Tray in the target + tray = GetService(typeof(ComponentTray)) as ComponentTray; + numberOfOriginalTrayControls = tray != null ? tray.Controls.Count : 0; + // We understand two things: CF_DESIGNER, and toolbox items. + object data = dataObj.GetData(CF_DESIGNER); + using (DesignerTransaction trans = host.CreateTransaction(SR.CommandSetPaste)) + { + if (data is byte[] bytes) + { + MemoryStream s = new MemoryStream(bytes); + if (s != null) + { + // CF_DESIGNER was put on the clipboard by us using the designer serialization service. + IDesignerSerializationService ds = (IDesignerSerializationService)GetService(typeof(IDesignerSerializationService)); + if (ds != null) + { + BinaryFormatter formatter = new BinaryFormatter(); + s.Seek(0, SeekOrigin.Begin); + object serializationData = formatter.Deserialize(s); + components = ds.Deserialize(serializationData); + } + } + } + else + { + // Now check for a toolbox item. + IToolboxService ts = (IToolboxService)GetService(typeof(IToolboxService)); + if (ts != null && ts.IsSupported(dataObj, host)) + { + ToolboxItem ti = ts.DeserializeToolboxItem(dataObj, host); + if (ti != null) + { + components = ti.CreateComponents(host); + createdItems = true; + } + } + } + + // Now, if we got some components, hook 'em up! + if (components != null && components.Count > 0) + { + IComponent curComp; + string name; + //Make copy of Items in Array.. + object[] allComponents = new object[components.Count]; + components.CopyTo(allComponents, 0); + ArrayList selectComps = new ArrayList(); + ArrayList controls = new ArrayList(); + string[] componentNames = null; + int idx = 0; + // if the selected item is a frame designer, add to that, otherwise add to the form + IComponent selectedComponent = null; + IDesigner designer = null; + bool dragClient = false; + + IComponent baseComponent = host.RootComponent; + selectedComponent = (IComponent)SelectionService.PrimarySelection; + if (selectedComponent == null) + { + selectedComponent = baseComponent; + } + + dragClient = false; + ITreeDesigner tree = host.GetDesigner(selectedComponent) as ITreeDesigner; + while (!dragClient && tree != null) + { + if (tree is IOleDragClient) + { + designer = tree; + dragClient = true; + } + else + { + if (tree == tree.Parent) + break; + tree = tree.Parent as ITreeDesigner; + } + } + + foreach (object obj in components) + { + + name = null; + curComp = obj as IComponent; + // see if we can fish out the original name. When we serialized, we serialized an array of names at the head of the list. This array matches the components that were created. + if (obj is IComponent) + { + if (componentNames != null && idx < componentNames.Length) + { + name = componentNames[idx++]; + } + } + else + { + if (componentNames == null && obj is string[] sa) + { + componentNames = sa; + idx = 0; + continue; + } + } + + if (GetService(typeof(IEventBindingService)) is IEventBindingService evs) + { + PropertyDescriptorCollection eventProps = evs.GetEventProperties(TypeDescriptor.GetEvents(curComp)); + foreach (PropertyDescriptor pd in eventProps) + { + // If we couldn't find a property for this event, or of the property is read only, then abort. + if (pd == null || pd.IsReadOnly) + { + continue; + } + + if (pd.GetValue(curComp) is string handler) + { + pd.SetValue(curComp, null); + } + } + } + + if (dragClient) + { + bool foundAssociatedControl = false; + // If we have failed to add a control in this Paste operation ... + if (associatedCompsOfFailedContol != null) + { + // then dont add its children controls. + foreach (Component comp in associatedCompsOfFailedContol) + { + if (comp == obj as Component) + { + foundAssociatedControl = true; + break; + } + } + } + + if (foundAssociatedControl) + { + continue; //continue from here so that we dont add the associted compoenet of a control that failed paste operation. + } + + ICollection designerComps = null; + // DGV has columns which are sited IComponents that don't have designers. in this case, ignore them. + if (!(host.GetDesigner(curComp) is ComponentDesigner cDesigner)) + { + continue; + } + //store associatedComponents. + designerComps = cDesigner.AssociatedComponents; + ComponentDesigner parentCompDesigner = ((ITreeDesigner)cDesigner).Parent as ComponentDesigner; + Component parentComp = null; + if (parentCompDesigner != null) + { + parentComp = parentCompDesigner.Component as Component; + } + + ArrayList associatedComps = new ArrayList(); + if (parentComp != null) + { + if (parentCompDesigner != null) + { + foreach (IComponent childComp in parentCompDesigner.AssociatedComponents) + { + associatedComps.Add(childComp as Component); + } + } + } + + if (parentComp == null || !(associatedComps.Contains(curComp))) + { + if (parentComp != null) + { + if (host.GetDesigner(parentComp) is ParentControlDesigner parentDesigner && !designerList.Contains(parentDesigner)) + { + parentDesigner.SuspendChangingEvents(); + designerList.Add(parentDesigner); + parentDesigner.ForceComponentChanging(); + } + } + + if (!((IOleDragClient)designer).AddComponent(curComp, name, createdItems)) + { + //cache the associatedComponents only for FAILED control. + associatedCompsOfFailedContol = designerComps; + // now we will jump out of the using block and call trans.Dispose() which in turn calls trans.Cancel for an uncommited transaction, We want to cancel the transaction because otherwise we'll have un-parented controls + return; + } + + Control designerControl = ((IOleDragClient)designer).GetControlForComponent(curComp); + if (designerControl != null) + { + controls.Add(designerControl); + } + // Select the newly Added top level component + if ((TypeDescriptor.GetAttributes(curComp).Contains(DesignTimeVisibleAttribute.Yes)) || curComp is ToolStripItem) + { + selectComps.Add(curComp); + } + } + // if Parent is not selected... select the curcomp. + else if (associatedComps.Contains(curComp) && Array.IndexOf(allComponents, parentComp) == -1) + { + selectComps.Add(curComp); + } + bool changeName = false; + if (curComp is Control c) + { + // if the text is the same as the name, remember it. After we add the control, we'll update the text with the new name. + if (name != null && name.Equals(c.Text)) + { + changeName = true; + } + } + if (changeName) + { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(curComp); + PropertyDescriptor nameProp = props["Name"]; + if (nameProp != null && nameProp.PropertyType == typeof(string)) + { + string newName = (string)nameProp.GetValue(curComp); + if (!newName.Equals(name)) + { + PropertyDescriptor textProp = props["Text"]; + if (textProp != null && textProp.PropertyType == nameProp.PropertyType) + { + textProp.SetValue(curComp, nameProp.GetValue(curComp)); + } + } + } + } + } + } + // Find those controls that have ControlDesigners and center them on the designer surface + ArrayList compsWithControlDesigners = new ArrayList(); + foreach (Control c in controls) + { + IDesigner des = host.GetDesigner((IComponent)c); + if (des is ControlDesigner) + { + compsWithControlDesigners.Add(c); + } + } + + if (compsWithControlDesigners.Count > 0) + { + // Update the control positions. We want to keep the entire block of controls relative to each other, but relocate them within the container. + UpdatePastePositions(compsWithControlDesigners); + } + + // Figure out if we added components to the component tray, and have the tray adjust their position. MartinTh - removed the old check, since ToolStrips breaks the scenario. ToolStrips have a ControlDesigner, but also add a component to the tray. The old code wouldn't detect that, so the tray location wouldn't get adjusted. Rather than fixing this up in ToolStripKeyboardHandlingService.OnCommandPaste, we do it here, since doing it in the service, wouldn't handle cross-form paste. + if (tray == null) + { + // the paste target did not have a tray already, so let's go get it - if there is one + tray = GetService(typeof(ComponentTray)) as ComponentTray; + } + if (tray != null) + { + int numberOfTrayControlsAdded = tray.Controls.Count - numberOfOriginalTrayControls; + if (numberOfTrayControlsAdded > 0) + { + ArrayList listOfTrayControls = new ArrayList(); + for (int i = 0; i < numberOfTrayControlsAdded; i++) + { + listOfTrayControls.Add(tray.Controls[numberOfOriginalTrayControls + i]); + } + tray.UpdatePastePositions(listOfTrayControls); + } + } + + // Update the tab indices of all the components. We must first sort the components by their existing tab indices or else we will not preserve their original intent. + controls.Sort(new TabIndexCompare()); + foreach (Control c in controls) + { + UpdatePasteTabIndex(c, c.Parent); + } + + // finally select all the components we added + SelectionService.SetSelectedComponents((object[])selectComps.ToArray(), SelectionTypes.Replace); + // and bring them to the front - but only if we can mess with the Z-order. + if (designer is ParentControlDesigner parentControlDesigner && parentControlDesigner.AllowSetChildIndexOnDrop) + { + MenuCommand btf = MenuService.FindCommand(MenuCommands.BringToFront); + if (btf != null) + { + btf.Invoke(); + } + } + trans.Commit(); + } + } + } + finally + { + Cursor.Current = oldCursor; + foreach (ParentControlDesigner des in designerList) + { + if (des != null) + { + des.ResumeChangingEvents(); + } + } + } + } + + /// + /// Called when the select all menu item is selected. + /// + protected void OnMenuSelectAll(object sender, EventArgs e) + { + Cursor oldCursor = Cursor.Current; + try + { + Cursor.Current = Cursors.WaitCursor; + if (site != null) + { + Debug.Assert(SelectionService != null, "We need the SelectionService, but we can't find it!"); + if (SelectionService == null) + { + return; + } + + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + if (host != null) + { + ComponentCollection components = host.Container.Components; + object[] selComps; + if (components == null || components.Count == 0) + { + selComps = new IComponent[0]; + } + else + { + selComps = new object[components.Count - 1]; + object baseComp = host.RootComponent; + + int j = 0; + foreach (IComponent comp in components) + { + if (baseComp == comp) + continue; + selComps[j++] = comp; + } + } + SelectionService.SetSelectedComponents(selComps, SelectionTypes.Replace); + } + } + } + finally + { + Cursor.Current = oldCursor; + } + } + + /// + /// Called when the show grid menu item is selected. + /// + protected void OnMenuShowGrid(object sender, EventArgs e) + { + if (site != null) + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + if (host != null) + { + DesignerTransaction trans = null; + + try + { + trans = host.CreateTransaction(); + IComponent baseComponent = host.RootComponent; + if (baseComponent != null && baseComponent is Control) + { + PropertyDescriptor prop = GetProperty(baseComponent, "DrawGrid"); + if (prop != null) + { + bool drawGrid = (bool)prop.GetValue(baseComponent); + prop.SetValue(baseComponent, !drawGrid); + ((MenuCommand)sender).Checked = !drawGrid; + } + } + } + finally + { + if (trans != null) + trans.Commit(); + } + } + } + } + + /// + /// Handles the various size to commands. + /// + protected void OnMenuSizingCommand(object sender, EventArgs e) + { + MenuCommand cmd = (MenuCommand)sender; + CommandID cmdID = cmd.CommandID; + if (SelectionService == null) + { + return; + } + + Cursor oldCursor = Cursor.Current; + try + { + Cursor.Current = Cursors.WaitCursor; + ICollection sel = SelectionService.GetSelectedComponents(); + object[] selectedObjects = new object[sel.Count]; + sel.CopyTo(selectedObjects, 0); + selectedObjects = FilterSelection(selectedObjects, SelectionRules.Visible); + object selPrimary = SelectionService.PrimarySelection; + Size primarySize = Size.Empty; + Size itemSize = Size.Empty; + PropertyDescriptor sizeProp; + if (selPrimary is IComponent component) + { + sizeProp = GetProperty(component, "Size"); + if (sizeProp == null) + { + //if we couldn't get a valid size for our primary selection, we'll fail silently + return; + } + primarySize = (Size)sizeProp.GetValue(component); + } + if (selPrimary == null) + { + return; + } + + Debug.Assert(null != selectedObjects, "queryStatus should have disabled this"); + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + DesignerTransaction trans = null; + try + { + if (host != null) + { + trans = host.CreateTransaction(string.Format(SR.CommandSetSize, selectedObjects.Length)); + } + + foreach (object obj in selectedObjects) + { + if (obj.Equals(selPrimary)) + continue; + if (!(obj is IComponent comp)) + { + continue; + } + //if the component is locked, no sizing is allowed... + PropertyDescriptor lockedDesc = GetProperty(obj, "Locked"); + if (lockedDesc != null && (bool)lockedDesc.GetValue(obj)) + { + continue; + } + sizeProp = GetProperty(comp, "Size"); + // Skip all components that don't have a size property + if (sizeProp == null || sizeProp.IsReadOnly) + { + continue; + } + itemSize = (Size)sizeProp.GetValue(comp); + if (cmdID == MenuCommands.SizeToControlHeight || cmdID == MenuCommands.SizeToControl) + { + itemSize.Height = primarySize.Height; + } + + if (cmdID == MenuCommands.SizeToControlWidth || cmdID == MenuCommands.SizeToControl) + { + itemSize.Width = primarySize.Width; + } + sizeProp.SetValue(comp, itemSize); + } + } + finally + { + if (trans != null) + { + trans.Commit(); + } + } + } + finally + { + Cursor.Current = oldCursor; + } + } + + /// + /// Called when the size->to grid menu item is selected. + /// + protected void OnMenuSizeToGrid(object sender, EventArgs e) + { + if (SelectionService == null) + { + return; + } + + Cursor oldCursor = Cursor.Current; + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + DesignerTransaction trans = null; + try + { + Cursor.Current = Cursors.WaitCursor; + ICollection sel = SelectionService.GetSelectedComponents(); + object[] selectedObjects = new object[sel.Count]; + sel.CopyTo(selectedObjects, 0); + selectedObjects = FilterSelection(selectedObjects, SelectionRules.Visible); + Size size = Size.Empty; + Point loc = Point.Empty; + Debug.Assert(null != selectedObjects, "queryStatus should have disabled this"); + Size grid = Size.Empty; + PropertyDescriptor sizeProp = null; + PropertyDescriptor locProp = null; + if (host != null) + { + trans = host.CreateTransaction(string.Format(SR.CommandSetSizeToGrid, selectedObjects.Length)); + IComponent baseComponent = host.RootComponent; + if (baseComponent != null && baseComponent is Control) + { + PropertyDescriptor prop = GetProperty(baseComponent, "CurrentGridSize"); + if (prop != null) + { + grid = (Size)prop.GetValue(baseComponent); + } + } + } + + if (!grid.IsEmpty) + { + foreach (object obj in selectedObjects) + { + IComponent comp = obj as IComponent; + if (obj == null) + { + continue; + } + + sizeProp = GetProperty(comp, "Size"); + locProp = GetProperty(comp, "Location"); + Debug.Assert(sizeProp != null, "No size property on component"); + Debug.Assert(locProp != null, "No location property on component"); + if (sizeProp == null || locProp == null || sizeProp.IsReadOnly || locProp.IsReadOnly) + { + continue; + } + + size = (Size)sizeProp.GetValue(comp); + loc = (Point)locProp.GetValue(comp); + size.Width = ((size.Width + (grid.Width / 2)) / grid.Width) * grid.Width; + size.Height = ((size.Height + (grid.Height / 2)) / grid.Height) * grid.Height; + loc.X = (loc.X / grid.Width) * grid.Width; + loc.Y = (loc.Y / grid.Height) * grid.Height; + sizeProp.SetValue(comp, size); + locProp.SetValue(comp, loc); + } + } + } + finally + { + if (trans != null) + { + trans.Commit(); + } + Cursor.Current = oldCursor; + } + } + + /// + /// Called when the properties menu item is selected on the Context menu + /// + protected void OnMenuDesignerProperties(object sender, EventArgs e) + { + // first, look if the currently selected object has a component editor... + object obj = SelectionService.PrimarySelection; + if (CheckComponentEditor(obj, true)) + { + return; + } + + IMenuCommandService menuSvc = (IMenuCommandService)GetService(typeof(IMenuCommandService)); + if (menuSvc != null) + { + if (menuSvc.GlobalInvoke(MenuCommands.PropertiesWindow)) + { + return; + } + } + Debug.Assert(false, "Invoking pbrs command failed"); + } + + /// + /// Called when the snap to grid menu item is selected. + /// + protected void OnMenuSnapToGrid(object sender, EventArgs e) + { + if (site != null) + { + IDesignerHost host = (IDesignerHost)site.GetService(typeof(IDesignerHost)); + if (host != null) + { + DesignerTransaction trans = null; + try + { + trans = host.CreateTransaction(string.Format(SR.CommandSetPaste, 0)); + IComponent baseComponent = host.RootComponent; + if (baseComponent != null && baseComponent is Control) + { + PropertyDescriptor prop = GetProperty(baseComponent, "SnapToGrid"); + if (prop != null) + { + bool snapToGrid = (bool)prop.GetValue(baseComponent); + prop.SetValue(baseComponent, !snapToGrid); + ((MenuCommand)sender).Checked = !snapToGrid; + } + } + } + finally + { + if (trans != null) + trans.Commit(); + } + } + } + + } + + /// + /// Called when a spacing command is selected + /// + protected void OnMenuSpacingCommand(object sender, EventArgs e) + { + MenuCommand cmd = (MenuCommand)sender; + CommandID cmdID = cmd.CommandID; + DesignerTransaction trans = null; + if (SelectionService == null) + { + return; + } + + Cursor oldCursor = Cursor.Current; + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + try + { + Cursor.Current = Cursors.WaitCursor; + // Inform the designer that we are about to monkey with a ton of properties. + Size grid = Size.Empty; + ICollection sel = SelectionService.GetSelectedComponents(); + object[] selectedObjects = new object[sel.Count]; + sel.CopyTo(selectedObjects, 0); + if (host != null) + { + trans = host.CreateTransaction(string.Format(SR.CommandSetFormatSpacing, selectedObjects.Length)); + IComponent baseComponent = host.RootComponent; + if (baseComponent != null && baseComponent is Control) + { + PropertyDescriptor prop = GetProperty(baseComponent, "CurrentGridSize"); + if (prop != null) + { + grid = (Size)prop.GetValue(baseComponent); + } + } + } + + selectedObjects = FilterSelection(selectedObjects, SelectionRules.Visible); + int nEqualDelta = 0; + Debug.Assert(null != selectedObjects, "queryStatus should have disabled this"); + PropertyDescriptor curSizeDesc = null, lastSizeDesc = null; + PropertyDescriptor curLocDesc = null, lastLocDesc = null; + Size curSize = Size.Empty, lastSize = Size.Empty; + Point curLoc = Point.Empty, lastLoc = Point.Empty; + Point primaryLoc = Point.Empty; + IComponent curComp = null, lastComp = null; + int sort = -1; + // Must sort differently if we're horizontal or vertical... + if (cmdID == MenuCommands.HorizSpaceConcatenate || cmdID == MenuCommands.HorizSpaceDecrease || cmdID == MenuCommands.HorizSpaceIncrease || cmdID == MenuCommands.HorizSpaceMakeEqual) + { + sort = SORT_HORIZONTAL; + } + else if (cmdID == MenuCommands.VertSpaceConcatenate || cmdID == MenuCommands.VertSpaceDecrease || cmdID == MenuCommands.VertSpaceIncrease || cmdID == MenuCommands.VertSpaceMakeEqual) + { + sort = SORT_VERTICAL; + } + else + { + throw new ArgumentException(SR.CommandSetUnknownSpacingCommand); + } + + SortSelection(selectedObjects, sort); + //now that we're sorted, lets get our primary selection and it's index + object primary = SelectionService.PrimarySelection; + int primaryIndex = 0; + if (primary != null) + primaryIndex = Array.IndexOf(selectedObjects, primary); + + // And compute delta values for Make Equal + if (cmdID == MenuCommands.HorizSpaceMakeEqual || + cmdID == MenuCommands.VertSpaceMakeEqual) + { + int total, n; + total = 0; + for (n = 0; n < selectedObjects.Length; n++) + { + curSize = Size.Empty; + if (selectedObjects[n] is IComponent component) + { + curComp = component; + curSizeDesc = GetProperty(curComp, "Size"); + if (curSizeDesc != null) + { + curSize = (Size)curSizeDesc.GetValue(curComp); + } + } + + if (sort == SORT_HORIZONTAL) + { + total += curSize.Width; + } + else + { + total += curSize.Height; + } + } + + lastComp = curComp = null; + curSize = Size.Empty; + curLoc = Point.Empty; + for (n = 0; n < selectedObjects.Length; n++) + { + curComp = selectedObjects[n] as IComponent; + if (curComp != null) + { + // only get the descriptors if we've changed component types + if (lastComp == null || curComp.GetType() != lastComp.GetType()) + { + curSizeDesc = GetProperty(curComp, "Size"); + curLocDesc = GetProperty(curComp, "Location"); + } + lastComp = curComp; + + if (curLocDesc != null) + { + curLoc = (Point)curLocDesc.GetValue(curComp); + } + else + { + continue; + } + + if (curSizeDesc != null) + { + curSize = (Size)curSizeDesc.GetValue(curComp); + } + else + { + continue; + } + + if (!curSize.IsEmpty && !curLoc.IsEmpty) + { + break; + } + } + } + + for (n = selectedObjects.Length - 1; n >= 0; n--) + { + curComp = selectedObjects[n] as IComponent; + if (curComp != null) + { + // only get the descriptors if we've changed component types + if (lastComp == null || curComp.GetType() != lastComp.GetType()) + { + curSizeDesc = GetProperty(curComp, "Size"); + curLocDesc = GetProperty(curComp, "Location"); + } + lastComp = curComp; + + if (curLocDesc != null) + { + lastLoc = (Point)curLocDesc.GetValue(curComp); + } + else + { + continue; + } + + if (curSizeDesc != null) + { + lastSize = (Size)curSizeDesc.GetValue(curComp); + } + else + { + continue; + } + + if (curSizeDesc != null && curLocDesc != null) + { + break; + } + } + } + + if (curSizeDesc != null && curLocDesc != null) + { + if (sort == SORT_HORIZONTAL) + { + nEqualDelta = (lastSize.Width + lastLoc.X - curLoc.X - total) / (selectedObjects.Length - 1); + } + else + { + nEqualDelta = (lastSize.Height + lastLoc.Y - curLoc.Y - total) / (selectedObjects.Length - 1); + } + if (nEqualDelta < 0) + nEqualDelta = 0; + } + } + curComp = lastComp = null; + if (primary != null) + { + PropertyDescriptor primaryLocDesc = GetProperty(primary, "Location"); + if (primaryLocDesc != null) + { + primaryLoc = (Point)primaryLocDesc.GetValue(primary); + } + } + + // Finally move the components + for (int n = 0; n < selectedObjects.Length; n++) + { + curComp = (IComponent)selectedObjects[n]; + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(curComp); + //Check to see if the component we are about to move is locked... + PropertyDescriptor lockedDesc = props["Locked"]; + if (lockedDesc != null && (bool)lockedDesc.GetValue(curComp)) + { + continue; // locked property of our component is true, so don't move it + } + + if (lastComp == null || lastComp.GetType() != curComp.GetType()) + { + curSizeDesc = props["Size"]; + curLocDesc = props["Location"]; + } + else + { + curSizeDesc = lastSizeDesc; + curLocDesc = lastLocDesc; + } + + if (curLocDesc != null) + { + curLoc = (Point)curLocDesc.GetValue(curComp); + } + else + { + continue; + } + + if (curSizeDesc != null) + { + curSize = (Size)curSizeDesc.GetValue(curComp); + } + else + { + continue; + } + + int lastIndex = Math.Max(0, n - 1); + lastComp = (IComponent)selectedObjects[lastIndex]; + if (lastComp.GetType() != curComp.GetType()) + { + lastSizeDesc = GetProperty(lastComp, "Size"); + lastLocDesc = GetProperty(lastComp, "Location"); + } + else + { + lastSizeDesc = curSizeDesc; + lastLocDesc = curLocDesc; + } + + if (lastLocDesc != null) + { + lastLoc = (Point)lastLocDesc.GetValue(lastComp); + } + else + { + continue; + } + + if (lastSizeDesc != null) + { + lastSize = (Size)lastSizeDesc.GetValue(lastComp); + } + else + { + continue; + } + + if (cmdID == MenuCommands.HorizSpaceConcatenate && n > 0) + { + curLoc.X = lastLoc.X + lastSize.Width; + } + else if (cmdID == MenuCommands.HorizSpaceDecrease) + { + if (primaryIndex < n) + { + curLoc.X -= grid.Width * (n - primaryIndex); + if (curLoc.X < primaryLoc.X) + curLoc.X = primaryLoc.X; + } + else if (primaryIndex > n) + { + curLoc.X += grid.Width * (primaryIndex - n); + if (curLoc.X > primaryLoc.X) + curLoc.X = primaryLoc.X; + } + } + else if (cmdID == MenuCommands.HorizSpaceIncrease) + { + if (primaryIndex < n) + { + curLoc.X += grid.Width * (n - primaryIndex); + } + else if (primaryIndex > n) + { + curLoc.X -= grid.Width * (primaryIndex - n); + } + + } + else if (cmdID == MenuCommands.HorizSpaceMakeEqual && n > 0) + { + curLoc.X = lastLoc.X + lastSize.Width + nEqualDelta; + } + else if (cmdID == MenuCommands.VertSpaceConcatenate && n > 0) + { + curLoc.Y = lastLoc.Y + lastSize.Height; + } + else if (cmdID == MenuCommands.VertSpaceDecrease) + { + if (primaryIndex < n) + { + curLoc.Y -= grid.Height * (n - primaryIndex); + if (curLoc.Y < primaryLoc.Y) + curLoc.Y = primaryLoc.Y; + } + else if (primaryIndex > n) + { + curLoc.Y += grid.Height * (primaryIndex - n); + if (curLoc.Y > primaryLoc.Y) + curLoc.Y = primaryLoc.Y; + } + } + else if (cmdID == MenuCommands.VertSpaceIncrease) + { + if (primaryIndex < n) + { + curLoc.Y += grid.Height * (n - primaryIndex); + } + else if (primaryIndex > n) + { + curLoc.Y -= grid.Height * (primaryIndex - n); + } + } + else if (cmdID == MenuCommands.VertSpaceMakeEqual && n > 0) + { + curLoc.Y = lastLoc.Y + lastSize.Height + nEqualDelta; + } + + if (!curLocDesc.IsReadOnly) + { + curLocDesc.SetValue(curComp, curLoc); + } + lastComp = curComp; + } + } + finally + { + if (trans != null) + { + trans.Commit(); + } + Cursor.Current = oldCursor; + } + } + + /// + /// Called when the current selection changes. Here we determine what commands can and can't be enabled. + /// + protected void OnSelectionChanged(object sender, EventArgs e) + { + if (SelectionService == null) + { + return; + } + _selectionVersion++; + // Update our cached selection counts. + selCount = SelectionService.SelectionCount; + IDesignerHost designerHost = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(designerHost != null, "Failed to get designer host"); + // if the base component is selected, we'll say that nothing's selected so we don't get wierd behavior + if (selCount > 0 && designerHost != null) + { + object baseComponent = designerHost.RootComponent; + if (baseComponent != null && SelectionService.GetComponentSelected(baseComponent)) + { + selCount = 0; + } + } + + primarySelection = SelectionService.PrimarySelection as IComponent; + _selectionInherited = false; + controlsOnlySelection = true; + if (selCount > 0) + { + ICollection selection = SelectionService.GetSelectedComponents(); + foreach (object obj in selection) + { + if (!(obj is Control)) + { + controlsOnlySelection = false; + } + + if (!TypeDescriptor.GetAttributes(obj)[typeof(InheritanceAttribute)].Equals(InheritanceAttribute.NotInherited)) + { + _selectionInherited = true; + break; + } + } + } + OnUpdateCommandStatus(); + } + + /// + /// When this timer expires, this tells us that we need to erase any snaplines we have drawn. First, we need to marshal this back to the correct thread. + /// + private void OnSnapLineTimerExpire(object sender, EventArgs e) + { + Control marshalControl = BehaviorService.AdornerWindowControl; + if (marshalControl != null && marshalControl.IsHandleCreated) + { + marshalControl.BeginInvoke(new EventHandler(OnSnapLineTimerExpireMarshalled), new object[] { sender, e }); + } + } + + /// + /// Called when our snapline timer expires - this method has been call has been properly marshalled back to the correct thread. + /// + private void OnSnapLineTimerExpireMarshalled(object sender, EventArgs e) + { + _snapLineTimer.Stop(); + EndDragManager(); + } + + /// + /// Determines the status of a menu command. Commands with this event handler are always enabled. + /// + protected void OnStatusAlways(object sender, EventArgs e) + { + MenuCommand cmd = (MenuCommand)sender; + cmd.Enabled = true; + } + + /// + /// Determines the status of a menu command. Commands with this event handler are enabled when one or more objects are selected. + /// + protected void OnStatusAnySelection(object sender, EventArgs e) + { + MenuCommand cmd = (MenuCommand)sender; + cmd.Enabled = selCount > 0; + } + + /// + /// Status for the copy command. This is enabled when there is something juicy selected. + /// + protected void OnStatusCopy(object sender, EventArgs e) + { + MenuCommand cmd = (MenuCommand)sender; + bool enable = false; + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (!_selectionInherited && host != null && !host.Loading) + { + ISelectionService selSvc = (ISelectionService)GetService(typeof(ISelectionService)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || selSvc != null, "ISelectionService not found"); + if (selSvc != null) + { + // There must also be a component in the mix, and not the base component + ICollection selectedComponents = selSvc.GetSelectedComponents(); + object baseComp = host.RootComponent; + if (!selSvc.GetComponentSelected(baseComp)) + { + foreach (object obj in selectedComponents) + { + // if the object is not sited to the same thing as the host container then don't allow copy. + if (obj is IComponent comp && comp.Site != null && comp.Site.Container == host.Container) + { + enable = true; + break; + } + } + } + } + } + cmd.Enabled = enable; + } + + /// + /// Status for the cut command. This is enabled when there is something juicy selected and that something does not contain any inherited components. + /// + protected void OnStatusCut(object sender, EventArgs e) + { + OnStatusDelete(sender, e); + if (((MenuCommand)sender).Enabled) + { + OnStatusCopy(sender, e); + } + } + + /// + /// Status for the delete command. This is enabled when there is something selected and that something does not contain inherited components. + /// + protected void OnStatusDelete(object sender, EventArgs e) + { + MenuCommand cmd = (MenuCommand)sender; + if (_selectionInherited) + { + cmd.Enabled = false; + } + else + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null) + { + ISelectionService selSvc = (ISelectionService)GetService(typeof(ISelectionService)); + if (selSvc != null) + { + ICollection selectedComponents = selSvc.GetSelectedComponents(); + foreach (object obj in selectedComponents) + { + // if the object is not sited to the same thing as the host container then don't allow delete. VSWhidbey# 275790 + if (obj is IComponent comp && (comp.Site == null || (comp.Site != null && comp.Site.Container != host.Container))) + { + cmd.Enabled = false; + return; + } + } + } + } + OnStatusAnySelection(sender, e); + } + } + + /// + /// Determines the status of a menu command. Commands with this event are enabled when there is something yummy on the clipboard. + /// + protected void OnStatusPaste(object sender, EventArgs e) + { + MenuCommand cmd = (MenuCommand)sender; + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + // Before we even look at the data format, check to see if the thing we're going to paste into is privately inherited. If it is, then we definitely cannot paste. + if (primarySelection != null) + { + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); + if (host != null && host.GetDesigner(primarySelection) is ParentControlDesigner) + { + // This component is a target for our paste operation. We must ensure that it is not privately inherited. + InheritanceAttribute attr = (InheritanceAttribute)TypeDescriptor.GetAttributes(primarySelection)[typeof(InheritanceAttribute)]; + Debug.Assert(attr != null, "Type descriptor gave us a null attribute -- problem in type descriptor"); + if (attr.InheritanceLevel == InheritanceLevel.InheritedReadOnly) + { + cmd.Enabled = false; + return; + } + } + } + + // Not being inherited. Now look at the contents of the data + IDataObject dataObj = Clipboard.GetDataObject(); + bool enable = false; + if (dataObj != null) + { + if (dataObj.GetDataPresent(CF_DESIGNER)) + { + enable = true; + } + else + { + // Not ours, check to see if the toolbox service understands this + IToolboxService ts = (IToolboxService)GetService(typeof(IToolboxService)); + if (ts != null) + { + enable = (host != null ? ts.IsSupported(dataObj, host) : ts.IsToolboxItem(dataObj)); + } + } + } + cmd.Enabled = enable; + } + + private void OnStatusPrimarySelection(object sender, EventArgs e) + { + MenuCommand cmd = (MenuCommand)sender; + cmd.Enabled = primarySelection != null; + } + + protected virtual void OnStatusSelectAll(object sender, EventArgs e) + { + MenuCommand cmd = (MenuCommand)sender; + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + cmd.Enabled = host.Container.Components.Count > 1; + } + + /// + /// This is called when the selection has changed. Anyone using CommandSetItems that need to update their status based on selection changes should override this and update their own commands at this time. The base implementaion runs through all base commands and calls UpdateStatus on them. + /// + protected virtual void OnUpdateCommandStatus() + { + // Now whip through all of the commands and ask them to update. + for (int i = 0; i < _commandSet.Length; i++) + { + _commandSet[i].UpdateStatus(); + } + } + + /// + /// This method grows the objects collection by one. It prepends the collection with a string[] which contains the component names in order for each component in the list. + /// + private ICollection PrependComponentNames(ICollection objects) + { + object[] newObjects = new object[objects.Count + 1]; + int idx = 1; + ArrayList names = new ArrayList(objects.Count); + foreach (object o in objects) + { + if (o is IComponent comp) + { + string name = null; + if (comp.Site != null) + { + name = comp.Site.Name; + } + names.Add(name); + } + newObjects[idx++] = o; + } + string[] nameArray = new string[names.Count]; + names.CopyTo(nameArray, 0); + newObjects[0] = nameArray; + return newObjects; + } + + /// + /// called by the formatting commands when we need a given selection array sorted. Sorting the array sorts by x from left to right, and by Y from top to bottom. + /// + private void SortSelection(object[] selectedObjects, int nSortBy) + { + IComparer comp; + switch (nSortBy) + { + case SORT_HORIZONTAL: + comp = new ComponentLeftCompare(); + break; + case SORT_VERTICAL: + comp = new ComponentTopCompare(); + break; + case SORT_ZORDER: + comp = new ControlZOrderCompare(); + break; + default: + return; + } + Array.Sort(selectedObjects, comp); + } + + /// + /// Common function that updates the status of clipboard menu items only + /// + private void UpdateClipboardItems(object s, EventArgs e) + { + int itemCount = 0; + CommandSetItem curItem; + for (int i = 0; itemCount < 3 && i < _commandSet.Length; i++) + { + curItem = _commandSet[i]; + if (curItem.CommandID == MenuCommands.Paste || + curItem.CommandID == MenuCommands.Copy || + curItem.CommandID == MenuCommands.Cut) + { + itemCount++; + curItem.UpdateStatus(); + } + } + } + + private void UpdatePastePositions(ArrayList controls) + { + if (controls.Count == 0) + { + return; + } + + // Find the offset to apply to these controls. The offset is the location needed to center the controls in the parent. If there is no parent, we relocate to 0, 0. + Control parentControl = ((Control)controls[0]).Parent; + Point min = ((Control)controls[0]).Location; + Point max = min; + foreach (Control c in controls) + { + Point loc = c.Location; + Size size = c.Size; + if (min.X > loc.X) + { + min.X = loc.X; + } + if (min.Y > loc.Y) + { + min.Y = loc.Y; + } + if (max.X < loc.X + size.Width) + { + max.X = loc.X + size.Width; + } + if (max.Y < loc.Y + size.Height) + { + max.Y = loc.Y + size.Height; + } + } + // We have the bounding rect for the controls. Next, offset this rect so that we center it in the parent. If we have no parent, the offset will position the control at 0, 0, to whatever parent we eventually get. + Point offset = new Point(-min.X, -min.Y); + // Look to ensure that we're not going to paste this control over the top of another control. We only do this for the first control because preserving the relationship between controls is more important than obscuring a control. + if (parentControl != null) + { + bool bumpIt; + bool wrapped = false; + Size parentSize = parentControl.ClientSize; + Size gridSize = Size.Empty; + Point parentOffset = new Point(parentSize.Width / 2, parentSize.Height / 2); + parentOffset.X -= (max.X - min.X) / 2; + parentOffset.Y -= (max.Y - min.Y) / 2; + do + { + bumpIt = false; + // Cycle through the controls on the parent. We're interested in controls that (a) are not in our set of controls and (b) have a location == to our current bumpOffset OR (c) are the same size as our parent. If we find such a control, we increment the bump offset by one grid size. + foreach (Control child in parentControl.Controls) + { + Rectangle childBounds = child.Bounds; + if (controls.Contains(child)) + { + // We still want to bump if the child is the same size as the parent. Otherwise the child would overlay exactly on top of the parent. + if (!child.Size.Equals(parentSize)) + { + continue; + } + + // We're dealing with our own pasted control, so offset its bounds. We don't use parent offset here because, well, we're comparing against the parent! + childBounds.Offset(offset); + } + + // We need only compare against one of our pasted controls, so pick the first one. + Control pasteControl = (Control)controls[0]; + Rectangle pasteControlBounds = pasteControl.Bounds; + pasteControlBounds.Offset(offset); + pasteControlBounds.Offset(parentOffset); + if (pasteControlBounds.Equals(childBounds)) + { + bumpIt = true; + if (gridSize.IsEmpty) + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + IComponent baseComponent = host.RootComponent; + if (baseComponent != null && baseComponent is Control) + { + PropertyDescriptor gs = GetProperty(baseComponent, "GridSize"); + if (gs != null) + { + gridSize = (Size)gs.GetValue(baseComponent); + } + } + if (gridSize.IsEmpty) + { + gridSize.Width = 8; + gridSize.Height = 8; + } + } + + parentOffset += gridSize; + // Extra check: If the end of our control group is > the parent size, bump back to zero. We still allow further bumps after this so we can continue to offset, but if we cycle again then we quit so we won't loop indefinitely. We only do this if we're a group. If we're a single control we use the beginning of the control + a grid size. + int groupEndX; + int groupEndY; + if (controls.Count > 1) + { + groupEndX = parentOffset.X + max.X - min.X; + groupEndY = parentOffset.Y + max.Y - min.Y; + } + else + { + groupEndX = parentOffset.X + gridSize.Width; + groupEndY = parentOffset.Y + gridSize.Height; + } + + if (groupEndX > parentSize.Width || groupEndY > parentSize.Height) + { + parentOffset.X = 0; + parentOffset.Y = 0; + if (wrapped) + { + bumpIt = false; + } + else + { + wrapped = true; + } + } + break; + } + } + } while (bumpIt); + offset.Offset(parentOffset.X, parentOffset.Y); + } + + // Now, for each control, update the offset. + if (parentControl != null) + { + parentControl.SuspendLayout(); + } + try + { + foreach (Control c in controls) + { + Point newLoc = c.Location; + newLoc.Offset(offset.X, offset.Y); + c.Location = newLoc; + } + } + finally + { + if (parentControl != null) + { + parentControl.ResumeLayout(); + } + } + } + + private void UpdatePasteTabIndex(Control componentControl, object parentComponent) + { + if (!(parentComponent is Control parentControl) || componentControl == null) + { + return; + } + bool tabIndexCollision = false; + int tabIndexOriginal = componentControl.TabIndex; + // Find the next highest tab index + int nextTabIndex = 0; + foreach (Control c in parentControl.Controls) + { + int t = c.TabIndex; + if (nextTabIndex <= t) + { + nextTabIndex = t + 1; + } + + if (t == tabIndexOriginal) + { + tabIndexCollision = true; + } + } + + if (tabIndexCollision) + { + componentControl.TabIndex = nextTabIndex; + } + } + + /// + /// We extend MenuCommand for our command set items. A command set item is a menu command with an added delegate that is used to determine the flags for the menu item. We have different classes of delegates here. For example, many menu items may be enabled when there is at least one object selected, while others are only enabled if there is more than one object or if there is a primary selection. + /// + protected class CommandSetItem : MenuCommand + { + private readonly EventHandler _statusHandler; + private readonly IEventHandlerService _eventService; + private readonly IUIService _uiService; + private readonly CommandSet _commandSet; + private static Hashtable s_commandStatusHash; // list of the command statuses we are tracking. + private bool _updatingCommand = false; // flag we set when we're updating the command so we don't call back on the status handler. + + public CommandSetItem(CommandSet commandSet, EventHandler statusHandler, EventHandler invokeHandler, CommandID id, IUIService uiService) : this(commandSet, statusHandler, invokeHandler, id, false, uiService) + { + } + + public CommandSetItem(CommandSet commandSet, EventHandler statusHandler, EventHandler invokeHandler, CommandID id) : this(commandSet, statusHandler, invokeHandler, id, false, null) + { + } + + public CommandSetItem(CommandSet commandSet, EventHandler statusHandler, EventHandler invokeHandler, CommandID id, bool optimizeStatus) : this(commandSet, statusHandler, invokeHandler, id, optimizeStatus, null) + { + } + + /// + /// Creates a new CommandSetItem. + /// + [SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity")] + public CommandSetItem(CommandSet commandSet, EventHandler statusHandler, EventHandler invokeHandler, CommandID id, bool optimizeStatus, IUIService uiService) + : base(invokeHandler, id) + { + _uiService = uiService; + _eventService = commandSet._eventService; + _statusHandler = statusHandler; + // when we optimize, it's because status is fully based on selection. so what we do is only call the status handler once per selection change to prevent doing the same work over and over again. we do this by hashing up the command statuses and then filling in the results we get, so we can easily retrieve them when the selection hasn't changed. + if (optimizeStatus && statusHandler != null) + { + // we use this as our sentinel of when we're doing this. + _commandSet = commandSet; + // create the hash if needed. + lock (typeof(CommandSetItem)) + { + if (s_commandStatusHash == null) + { + s_commandStatusHash = new Hashtable(); + } + } + + // UNDONE:CommandSetItem is put in a static hashtable, and CommandSetItem references CommandSet, CommandSet reference FormDesigner. If we don't remove the CommandSetItem from the static hashtable, FormDesigner is leaked. This demonstrates a bad design. We should not keep a static hashtable for all the items, instead, we should keep a hashtable per Designer. When designer is disposed, all command items got disposed automatically. However, at this time, we would pick a simple way with low risks to fix this. + // if this handler isn't already in there, add it. + if (!(s_commandStatusHash[statusHandler] is StatusState state)) + { + state = new StatusState(); + s_commandStatusHash.Add(statusHandler, state); + } + state.refCount++; + } + } + + /// + /// Checks if the status for this command is valid, meaning we don't need to call the status handler. + /// + private bool CommandStatusValid + { + get + { + // check to see if this is a command we have hashed up and if it's version stamp is the same as our current selection version. + if (_commandSet != null && s_commandStatusHash.Contains(_statusHandler)) + { + if (s_commandStatusHash[_statusHandler] is StatusState state && state.SelectionVersion == _commandSet.SelectionVersion) + { + return true; + } + } + return false; + } + } + + /// + /// Applys the cached status to this item. + /// + private void ApplyCachedStatus() + { + if (_commandSet != null && s_commandStatusHash.Contains(_statusHandler)) + { + try + { + // set our our updating flag so it doesn't call the status handler again. + _updatingCommand = true; + // and push the state into this command. + StatusState state = s_commandStatusHash[_statusHandler] as StatusState; + state.ApplyState(this); + } + finally + { + _updatingCommand = false; + } + } + } + + /// + /// This may be called to invoke the menu item. + /// + public override void Invoke() + { + // We allow outside parties to override the availability of particular menu commands. + try + { + if (_eventService != null) + { + IMenuStatusHandler msh = (IMenuStatusHandler)_eventService.GetHandler(typeof(IMenuStatusHandler)); + if (msh != null && msh.OverrideInvoke(this)) + { + return; + } + } + base.Invoke(); + } + catch (Exception e) + { + if (_uiService != null) + { + _uiService.ShowError(e, string.Format(SR.CommandSetError, e.Message)); + } + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + } + } + + /// + /// Only pass this down to the base when we're not doing the cached update. + /// + protected override void OnCommandChanged(EventArgs e) + { + if (!_updatingCommand) + { + base.OnCommandChanged(e); + } + } + + /// + /// Saves the status for this command to the statusstate that's stored in the hashtable based on our status handler delegate. + /// + private void SaveCommandStatus() + { + if (_commandSet != null) + { + StatusState state; + // see if we need to create one of these StatusState dudes. + if (s_commandStatusHash.Contains(_statusHandler)) + { + state = s_commandStatusHash[_statusHandler] as StatusState; + } + else + { + state = new StatusState(); + } + // and save the enabled, visible, checked, and supported state. + state.SaveState(this, _commandSet.SelectionVersion); + } + } + + /// + /// Called when the status of this command should be re-queried. + /// + public void UpdateStatus() + { + // We allow outside parties to override the availability of particular menu commands. + if (_eventService != null) + { + IMenuStatusHandler msh = (IMenuStatusHandler)_eventService.GetHandler(typeof(IMenuStatusHandler)); + if (msh != null && msh.OverrideStatus(this)) + { + return; + } + } + if (_statusHandler != null) + { + // if we need to update our status, call the status handler. otherwise, get the cached status and push it into this command. + if (!CommandStatusValid) + { + try + { + _statusHandler(this, EventArgs.Empty); + SaveCommandStatus(); + } + catch + { + } + } + else + { + ApplyCachedStatus(); + } + } + } + + /// + /// Remove this command item from the static hashtable to avoid leaking this object. + /// + public virtual void Dispose() + { + if (s_commandStatusHash[_statusHandler] is StatusState state) + { + state.refCount--; + if (state.refCount == 0) + { + s_commandStatusHash.Remove(_statusHandler); + } + } + } + + /// + /// This class saves the state for a given command. It keeps track of the results of the last status handler invocation and what "selection version" that happened on. + /// + private class StatusState + { + // these are the command's possible values. + private const int Enabled = 0x01; + private const int Visible = 0x02; + private const int Checked = 0x04; + private const int Supported = 0x08; + private const int NeedsUpdate = 0x10; + private int _selectionVersion = 0; // the version of the selection that this was initialized with. + private int _statusFlags = NeedsUpdate; // our flags. + // Multiple CommandSetItem instances can share a same status handler within a designer host. We use a simple ref count to make sure the CommandSetItem can be properly removed. + internal int refCount = 0; + + /// + /// Just what it says... + /// + public int SelectionVersion + { + get => _selectionVersion; + } + + /// + /// Pushes the state stored in this object into the given command item. + /// + internal void ApplyState(CommandSetItem item) + { + Debug.Assert((_statusFlags & NeedsUpdate) != NeedsUpdate, "Updating item when StatusState is not valid."); + item.Enabled = ((_statusFlags & Enabled) == Enabled); + item.Visible = ((_statusFlags & Visible) == Visible); + item.Checked = ((_statusFlags & Checked) == Checked); + item.Supported = ((_statusFlags & Supported) == Supported); + } + + /// + /// Updates this status object with the state from the given item, and saves teh seletion version. + /// + internal void SaveState(CommandSetItem item, int version) + { + _selectionVersion = version; + _statusFlags = 0; + if (item.Enabled) + { + _statusFlags |= Enabled; + } + if (item.Visible) + { + _statusFlags |= Visible; + } + if (item.Checked) + { + _statusFlags |= Checked; + } + if (item.Supported) + { + _statusFlags |= Supported; + } + } + } + } + + /// + /// The immediate command set item is used for commands that cannot be cached. Commands such as Paste that get outside stimulus cannot be cached by our menu system, so they get an ImmediateCommandSetItem instead of a CommandSetItem. + /// + protected class ImmediateCommandSetItem : CommandSetItem + { + /// + /// Creates a new ImmediateCommandSetItem. + /// + public ImmediateCommandSetItem(CommandSet commandSet, EventHandler statusHandler, EventHandler invokeHandler, CommandID id, IUIService uiService) : base(commandSet, statusHandler, invokeHandler, id, uiService) + { + } + + /// + /// Overrides OleStatus in MenuCommand to invoke our status handler first. + /// + public override int OleStatus + { + get + { + UpdateStatus(); + return base.OleStatus; + } + } + } + + /// + /// Component comparer that compares the left property of a component. + /// + private class ComponentLeftCompare : IComparer + { + public int Compare(object p, object q) + { + PropertyDescriptor pProp = TypeDescriptor.GetProperties(p)["Location"]; + PropertyDescriptor qProp = TypeDescriptor.GetProperties(q)["Location"]; + Point pLoc = (Point)pProp.GetValue(p); + Point qLoc = (Point)qProp.GetValue(q); + //if our lefts are equal, then compare tops + if (pLoc.X == qLoc.X) + { + return pLoc.Y - qLoc.Y; + } + return pLoc.X - qLoc.X; + } + } + + /// + /// Component comparer that compares the top property of a component. + /// + private class ComponentTopCompare : IComparer + { + public int Compare(object p, object q) + { + PropertyDescriptor pProp = TypeDescriptor.GetProperties(p)["Location"]; + PropertyDescriptor qProp = TypeDescriptor.GetProperties(q)["Location"]; + Point pLoc = (Point)pProp.GetValue(p); + Point qLoc = (Point)qProp.GetValue(q); + //if our tops are equal, then compare lefts + if (pLoc.Y == qLoc.Y) + { + return pLoc.X - qLoc.X; + } + return pLoc.Y - qLoc.Y; + } + } + + private class ControlZOrderCompare : IComparer + { + public int Compare(object p, object q) + { + if (p == null) + { + return -1; + } + else if (q == null) + { + return 1; + } + else if (p == q) + { + return 0; + } + if (!(p is Control c1) || !(q is Control c2)) + { + return 1; + } + + if (c1.Parent == c2.Parent && c1.Parent != null) + { + return c1.Parent.Controls.GetChildIndex(c1) - c1.Parent.Controls.GetChildIndex(c2); + } + return 1; + } + } + + private class TabIndexCompare : IComparer + { + public int Compare(object p, object q) + { + Control c1 = p as Control; + Control c2 = q as Control; + if (c1 == c2) + { + return 0; + } + + if (c1 == null) + { + return -1; + } + + if (c2 == null) + { + return 1; + } + return c1.TabIndex - c2.TabIndex; + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ComponentTray.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ComponentTray.cs index 891824abe8d..cd85da2e5a9 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ComponentTray.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ComponentTray.cs @@ -2,18 +2,24 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections; using System.ComponentModel; using System.ComponentModel.Design; +using System.ComponentModel.Design.Serialization; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Design; +using System.Drawing.Drawing2D; +using System.IO; +using System.Runtime.InteropServices; +using System.Windows.Forms.Design.Behavior; +using Microsoft.Win32; namespace System.Windows.Forms.Design { /// - /// - /// Provides the component tray UI for the form designer. - /// + /// Provides the component tray UI for the form designer. /// [ToolboxItem(false)] [DesignTimeVisible(false)] @@ -21,202 +27,926 @@ namespace System.Windows.Forms.Design [ProvideProperty("TrayLocation", typeof(IComponent))] public class ComponentTray : ScrollableControl, IExtenderProvider, ISelectionUIHandler, IOleDragClient { + private static readonly Point InvalidPoint = new Point(int.MinValue, int.MinValue); + private IServiceProvider serviceProvider; // Where services come from. + private Point whiteSpace = Point.Empty; // space to leave between components. + private Size grabHandle = Size.Empty; // Size of the grab handles. + + private ArrayList controls; // List of items in the tray in the order of their layout. + private SelectionUIHandler dragHandler; // the thing responsible for handling mouse drags + private ISelectionUIService selectionUISvc; // selectiuon UI; we use this a lot + private IToolboxService toolboxService; // cached for drag/drop + + /// + /// Provides drag and drop functionality through OLE. + /// + internal OleDragDropHandler oleDragDropHandler; // handler class for ole drag drop operations. + + private IDesigner mainDesigner; // the designer that is associated with this tray + private IEventHandlerService eventHandlerService = null; // Event Handler service to handle keyboard and focus. + private bool queriedTabOrder; + private MenuCommand tabOrderCommand; + private ICollection selectedObjects; + + // Services that we use on a high enough frequency to merit caching. + private IMenuCommandService menuCommandService; + private CommandSet privateCommandSet = null; + private InheritanceUI inheritanceUI; + + private Point mouseDragStart = InvalidPoint; // the starting location of a drag + private Point mouseDragEnd = InvalidPoint; // the ending location of a drag + private Rectangle mouseDragWorkspace = Rectangle.Empty; // a temp work rectangle we cache for perf + private ToolboxItem mouseDragTool; // the tool that's being dragged; only for drag/drop + private Point mouseDropLocation = InvalidPoint; // where the tool was dropped + private bool showLargeIcons = false;// Show Large icons or not. + private bool autoArrange = false; // allows for auto arranging icons. + private Point autoScrollPosBeforeDragging = Point.Empty;//Used to return the correct scroll pos. after a drag + + // Component Tray Context menu items... + private MenuCommand menucmdArrangeIcons = null; + private MenuCommand menucmdLineupIcons = null; + private MenuCommand menucmdLargeIcons = null; + private bool fResetAmbient = false; + private bool fSelectionChanged = false; + private ComponentTrayGlyphManager glyphManager;//used to manage any glyphs added to the tray + // Empty class for build time dependancy /// - /// Creates a new component tray. The component tray - /// will monitor component additions and removals and create - /// appropriate UI objects in its space. + /// Creates a new component tray. The component tray + /// will monitor component additions and removals and create + /// appropriate UI objects in its space. /// [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] public ComponentTray(IDesigner mainDesigner, IServiceProvider serviceProvider) { - throw new NotImplementedException(SR.NotImplementedByDesign); + AutoScroll = true; + this.mainDesigner = mainDesigner; + this.serviceProvider = serviceProvider; + AllowDrop = true; + Text = "ComponentTray"; // makes debugging easier + SetStyle(ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true); + controls = new ArrayList(); + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + IExtenderProviderService es = (IExtenderProviderService)GetService(typeof(IExtenderProviderService)); + Debug.Assert(es != null, "Component tray wants an extender provider service, but there isn't one."); + if (es != null) + { + es.AddExtenderProvider(this); + } + + if (GetService(typeof(IEventHandlerService)) == null) + { + if (host != null) + { + eventHandlerService = new EventHandlerService(this); + host.AddService(typeof(IEventHandlerService), eventHandlerService); + } + } + + IMenuCommandService mcs = MenuService; + if (mcs != null) + { + Debug.Assert(menucmdArrangeIcons == null, "Non-Null Menu Command for ArrangeIcons"); + Debug.Assert(menucmdLineupIcons == null, "Non-Null Menu Command for LineupIcons"); + Debug.Assert(menucmdLargeIcons == null, "Non-Null Menu Command for LargeIcons"); + menucmdArrangeIcons = new MenuCommand(new EventHandler(OnMenuArrangeIcons), StandardCommands.ArrangeIcons); + menucmdLineupIcons = new MenuCommand(new EventHandler(OnMenuLineupIcons), StandardCommands.LineupIcons); + menucmdLargeIcons = new MenuCommand(new EventHandler(OnMenuShowLargeIcons), StandardCommands.ShowLargeIcons); + menucmdArrangeIcons.Checked = AutoArrange; + menucmdLargeIcons.Checked = ShowLargeIcons; + mcs.AddCommand(menucmdArrangeIcons); + mcs.AddCommand(menucmdLineupIcons); + mcs.AddCommand(menucmdLargeIcons); + } + + IComponentChangeService componentChangeService = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (componentChangeService != null) + { + componentChangeService.ComponentRemoved += new ComponentEventHandler(OnComponentRemoved); + } + + if (GetService(typeof(IUIService)) is IUIService uiService) + { + Color styleColor; + if (uiService.Styles["ArtboardBackground"] is Color) + { + styleColor = (Color)uiService.Styles["ArtboardBackground"]; + } + //Can't use 'as' here since Color is a value type + else if (uiService.Styles["VsColorDesignerTray"] is Color) + { + styleColor = (Color)uiService.Styles["VsColorDesignerTray"]; + } + else if (uiService.Styles["HighlightColor"] is Color) + { + // Since v1, we have had code here that checks for HighlightColor, so some hosts (like WinRes) have been setting it. If VsColorDesignerTray isn't present, we look for HighlightColor for backward compat. + styleColor = (Color)uiService.Styles["HighlightColor"]; + } + else + { + //No style color provided? Let's pick a default. + styleColor = SystemColors.Info; + } + + if (uiService.Styles["ArtboardBackgroundText"] is Color) + { + ForeColor = (Color)uiService.Styles["ArtboardBackgroundText"]; + } + else if (uiService.Styles["VsColorPanelText"] is Color) + { + ForeColor = (Color)uiService.Styles["VsColorPanelText"]; + } + + BackColor = styleColor; + Font = (Font)uiService.Styles["DialogFont"]; + } + + ISelectionService selSvc = (ISelectionService)GetService(typeof(ISelectionService)); + if (selSvc != null) + { + selSvc.SelectionChanged += new EventHandler(OnSelectionChanged); + } + + // Listen to the SystemEvents so that we can resync selection based on display settings etc. + SystemEvents.DisplaySettingsChanged += new EventHandler(OnSystemSettingChanged); + SystemEvents.InstalledFontsChanged += new EventHandler(OnSystemSettingChanged); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + + // Listen to refresh events from TypeDescriptor. If a component gets refreshed, we re-query and will hide/show the view based on the DesignerView attribute. + TypeDescriptor.Refreshed += new RefreshEventHandler(OnComponentRefresh); + + if (GetService(typeof(BehaviorService)) is BehaviorService behSvc) + { + //this object will manage any glyphs that get added to our tray + glyphManager = new ComponentTrayGlyphManager(selSvc, behSvc); + } } - public bool AutoArrange + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { - get => throw new NotImplementedException(SR.NotImplementedByDesign); + if (IsHandleCreated) + { + fResetAmbient = true; + ResetTrayControls(); + BeginInvoke(new AsyncInvokeHandler(Invalidate), new object[] { true }); + } + } - set => throw new NotImplementedException(SR.NotImplementedByDesign); + private void OnComponentRefresh(RefreshEventArgs e) + { + if (e.ComponentChanged is IComponent component) + { + TrayControl control = TrayControl.FromComponent(component); + if (control != null) + { + bool shouldDisplay = CanDisplayComponent(component); + if (shouldDisplay != control.Visible || !shouldDisplay) + { + control.Visible = shouldDisplay; + Rectangle bounds = control.Bounds; + bounds.Inflate(grabHandle); + bounds.Inflate(grabHandle); + Invalidate(bounds); + PerformLayout(); + } + } + } } - /// - /// - /// Gets the number of compnents contained within this tray. - /// - /// - public int ComponentCount => throw new NotImplementedException(SR.NotImplementedByDesign); + private void OnSystemSettingChanged(object sender, EventArgs e) + { + if (IsHandleCreated) + { + fResetAmbient = true; + ResetTrayControls(); + BeginInvoke(new AsyncInvokeHandler(Invalidate), new object[] { true }); + } + } - /// - /// Determines whether the tray will show large icon view or not. - /// - public bool ShowLargeIcons + private void ResetTrayControls() { - get => throw new NotImplementedException(SR.NotImplementedByDesign); + ControlCollection children = (ControlCollection)Controls; + if (children == null) + return; - set => throw new NotImplementedException(SR.NotImplementedByDesign); + for (int i = 0; i < children.Count; ++i) + { + if (children[i] is TrayControl tc) + { + tc._fRecompute = true; + } + } } - bool IExtenderProvider.CanExtend(object extendee) + private delegate void AsyncInvokeHandler(bool children); + + private void OnSelectionChanged(object sender, EventArgs e) { - throw new NotImplementedException(); + selectedObjects = ((ISelectionService)sender).GetSelectedComponents(); + object primary = ((ISelectionService)sender).PrimarySelection; + Invalidate(); + fSelectionChanged = true; + // Accessibility information + foreach (object selObj in selectedObjects) + { + if (selObj is IComponent component) + { + Control c = TrayControl.FromComponent(component); + if (c != null) + { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "MSAA: SelectionAdd, traycontrol = " + c.ToString()); + UnsafeNativeMethods.NotifyWinEvent((int)AccessibleEvents.SelectionAdd, new HandleRef(c, c.Handle), NativeMethods.OBJID_CLIENT, 0); + } + } + } + + if (primary is IComponent comp) + { + Control c = TrayControl.FromComponent(comp); + if (c != null && IsHandleCreated) + { + ScrollControlIntoView(c); + UnsafeNativeMethods.NotifyWinEvent((int)AccessibleEvents.Focus, new HandleRef(c, c.Handle), NativeMethods.OBJID_CLIENT, 0); + } + if (glyphManager != null) + { + glyphManager.SelectionGlyphs.Clear(); + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + foreach (object selObj in selectedObjects) + { + if (selObj is IComponent selectedComponent && !(host.GetDesigner(selectedComponent) is ControlDesigner)) + { // don't want to do it for controls that are also in the tray + GlyphCollection glyphs = glyphManager.GetGlyphsForComponent(selectedComponent); + if (glyphs != null && glyphs.Count > 0) + { + SelectionGlyphs.AddRange(glyphs); + } + } + } + } + } } - IComponent IOleDragClient.Component => throw new NotImplementedException(); + private void OnComponentRemoved(object sender, ComponentEventArgs cevent) + { + RemoveComponent(cevent.Component); + } - bool IOleDragClient.CanModifyComponents => throw new NotImplementedException(); + private void OnMenuShowLargeIcons(object sender, EventArgs e) + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + DesignerTransaction t = null; + try + { + t = host.CreateTransaction(SR.TrayShowLargeIcons); + PropertyDescriptor trayIconProp = TypeDescriptor.GetProperties(mainDesigner.Component)["TrayLargeIcon"]; + if (trayIconProp != null) + { + trayIconProp.SetValue(mainDesigner.Component, !ShowLargeIcons); + } + } + finally + { + if (t != null) + t.Commit(); + } + } - bool IOleDragClient.AddComponent(IComponent component, string name, bool firstAdd) + private void OnMenuLineupIcons(object sender, EventArgs e) { - throw new NotImplementedException(); + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + DesignerTransaction t = null; + try + { + t = host.CreateTransaction(SR.TrayLineUpIcons); + DoLineupIcons(); + } + finally + { + if (t != null) + t.Commit(); + } } - bool IOleDragClient.IsDropOk(IComponent component) + private void DoLineupIcons() { - throw new NotImplementedException(); + if (autoArrange) + return; + bool oldValue = autoArrange; + autoArrange = true; + try + { + DoAutoArrange(true); + } + finally + { + autoArrange = oldValue; + } } - Control IOleDragClient.GetDesignerControl() + private void DoAutoArrange(bool dirtyDesigner) { - throw new NotImplementedException(); + if (controls == null || controls.Count <= 0) + { + return; + } + + controls.Sort(new AutoArrangeComparer()); + + SuspendLayout(); + + //Reset the autoscroll position before auto arranging. + //This way, when OnLayout gets fired after this, we won't + //have to move every component again. Note that sync'ing + //the selection will automatically select & scroll into view + //the right components + AutoScrollPosition = new Point(0, 0); + + try + { + Control prevCtl = null; + bool positionedGlobal = true; + foreach (Control ctl in controls) + { + if (!ctl.Visible) + continue; + // If we're auto arranging, always move the control. If not, move the control only if it was never given a position. This auto arranges it until the user messes with it, or until its position is saved into the resx. (if one control is no longer positioned, move all the other one as we don't want them to go under one another) + if (autoArrange) + { + PositionInNextAutoSlot(ctl as TrayControl, prevCtl, dirtyDesigner); + } + else if (!((TrayControl)ctl).Positioned || !positionedGlobal) + { + PositionInNextAutoSlot(ctl as TrayControl, prevCtl, false); + positionedGlobal = false; + } + prevCtl = ctl; + } + if (selectionUISvc != null) + { + selectionUISvc.SyncSelection(); + } + } + finally + { + ResumeLayout(); + } } - Control IOleDragClient.GetControlForComponent(object component) + private void OnMenuArrangeIcons(object sender, EventArgs e) { - throw new NotImplementedException(); + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + DesignerTransaction t = null; + + try + { + t = host.CreateTransaction(SR.TrayAutoArrange); + + PropertyDescriptor trayAAProp = TypeDescriptor.GetProperties(mainDesigner.Component)["TrayAutoArrange"]; + if (trayAAProp != null) + { + trayAAProp.SetValue(mainDesigner.Component, !AutoArrange); + } + } + finally + { + if (t != null) + t.Commit(); + } } - bool ISelectionUIHandler.BeginDrag(object[] components, SelectionRules rules, int initialX, int initialY) + public bool AutoArrange { - throw new NotImplementedException(); + get => autoArrange; + set + { + if (autoArrange != value) + { + autoArrange = value; + menucmdArrangeIcons.Checked = value; + + if (autoArrange) + { + DoAutoArrange(true); + } + } + } } - void ISelectionUIHandler.DragMoved(object[] components, Rectangle offset) + /// + /// + /// Gets the number of compnents contained within this tray. + /// + /// + public int ComponentCount { - throw new NotImplementedException(); + get => Controls.Count; } - void ISelectionUIHandler.EndDrag(object[] components, bool cancel) + internal GlyphCollection SelectionGlyphs { - throw new NotImplementedException(); + get + { + if (glyphManager != null) + { + return glyphManager.SelectionGlyphs; + } + else + { + return null; + } + } } - Rectangle ISelectionUIHandler.GetComponentBounds(object component) + /// + /// Determines whether the tray will show large icon view or not. + /// + public bool ShowLargeIcons { - throw new NotImplementedException(); + get => showLargeIcons; + set + { + if (showLargeIcons != value) + { + showLargeIcons = value; + menucmdLargeIcons.Checked = ShowLargeIcons; + + ResetTrayControls(); + Invalidate(true); + } + } } - SelectionRules ISelectionUIHandler.GetComponentRules(object component) + bool IExtenderProvider.CanExtend(object extendee) { - throw new NotImplementedException(); + return (extendee is IComponent comp) && (TrayControl.FromComponent(comp) != null); } - Rectangle ISelectionUIHandler.GetSelectionClipRect(object component) + IComponent IOleDragClient.Component { - throw new NotImplementedException(); + get => mainDesigner.Component; } - void ISelectionUIHandler.OnSelectionDoubleClick(IComponent component) + bool IOleDragClient.CanModifyComponents { - throw new NotImplementedException(); + get => true; } - bool ISelectionUIHandler.QueryBeginDrag(object[] components, SelectionRules rules, int initialX, int initialY) + bool IOleDragClient.AddComponent(IComponent component, string name, bool firstAdd) { - throw new NotImplementedException(); + // the designer for controls decides what to do here + if (mainDesigner is IOleDragClient oleDragClient) + { + try + { + oleDragClient.AddComponent(component, name, firstAdd); + PositionControl(TrayControl.FromComponent(component)); + mouseDropLocation = InvalidPoint; + return true; + } + catch + { + } + } + else + { + // for webforms (98109) just add the component directly to the host + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + try + { + if (host != null && host.Container != null) + { + if (host.Container.Components[name] != null) + { + name = null; + } + host.Container.Add(component, name); + return true; + } + } + catch + { + } + } + Debug.Fail("Don't know how to add component!"); + return false; } - void ISelectionUIHandler.ShowContextMenu(IComponent component) + bool IOleDragClient.IsDropOk(IComponent component) => true; + + Control IOleDragClient.GetDesignerControl() => this; + + Control IOleDragClient.GetControlForComponent(object component) { - throw new NotImplementedException(); + if (component is IComponent comp) + { + return TrayControl.FromComponent(comp); + } + Debug.Fail("component is not IComponent"); + return null; } - void ISelectionUIHandler.OleDragEnter(DragEventArgs de) + bool ISelectionUIHandler.BeginDrag(object[] components, SelectionRules rules, int initialX, int initialY) { - throw new NotImplementedException(); + if (TabOrderActive) + { + return false; + } + bool result = DragHandler.BeginDrag(components, rules, initialX, initialY); + if (result) + { + if (!GetOleDragHandler().DoBeginDrag(components, rules, initialX, initialY)) + { + return false; + } + } + return result; } - void ISelectionUIHandler.OleDragDrop(DragEventArgs de) + internal virtual OleDragDropHandler GetOleDragHandler() { - throw new NotImplementedException(); + if (oleDragDropHandler == null) + { + oleDragDropHandler = new TrayOleDragDropHandler(DragHandler, serviceProvider, this); + } + return oleDragDropHandler; } - void ISelectionUIHandler.OleDragOver(DragEventArgs de) + internal virtual SelectionUIHandler DragHandler { - throw new NotImplementedException(); + get + { + if (dragHandler == null) + { + dragHandler = new TraySelectionUIHandler(this); + } + return dragHandler; + } } - void ISelectionUIHandler.OleDragLeave() + void ISelectionUIHandler.DragMoved(object[] components, Rectangle offset) => DragHandler.DragMoved(components, offset); + + void ISelectionUIHandler.EndDrag(object[] components, bool cancel) + { + DragHandler.EndDrag(components, cancel); + GetOleDragHandler().DoEndDrag(components, cancel); + // Here, after the drag is finished and after we have resumed layout, adjust the location of the components we dragged by the scroll offset + if (!autoScrollPosBeforeDragging.IsEmpty) + { + foreach (IComponent comp in components) + { + TrayControl tc = TrayControl.FromComponent(comp); + if (tc != null) + { + SetTrayLocation(comp, new Point(tc.Location.X - autoScrollPosBeforeDragging.X, tc.Location.Y - autoScrollPosBeforeDragging.Y)); + } + } + AutoScrollPosition = new Point(-autoScrollPosBeforeDragging.X, -autoScrollPosBeforeDragging.Y); + } + } + + // We render the selection UI glyph ourselves. + Rectangle ISelectionUIHandler.GetComponentBounds(object component) => Rectangle.Empty; + + SelectionRules ISelectionUIHandler.GetComponentRules(object component) => SelectionRules.Visible | SelectionRules.Moveable; + + Rectangle ISelectionUIHandler.GetSelectionClipRect(object component) + { + if (IsHandleCreated) + { + return RectangleToScreen(ClientRectangle); + } + return Rectangle.Empty; + } + + void ISelectionUIHandler.OnSelectionDoubleClick(IComponent component) + { + if (!TabOrderActive) + { + if (((IOleDragClient)this).GetControlForComponent(component) is TrayControl tc) + { + tc.ViewDefaultEvent(component); + } + } + } + + bool ISelectionUIHandler.QueryBeginDrag(object[] components, SelectionRules rules, int initialX, int initialY) => DragHandler.QueryBeginDrag(components, rules, initialX, initialY); + + void ISelectionUIHandler.ShowContextMenu(IComponent component) + { + Point cur = Control.MousePosition; + OnContextMenu(cur.X, cur.Y, true); + } + + private void OnContextMenu(int x, int y, bool useSelection) { - throw new NotImplementedException(); + if (!TabOrderActive) + { + Capture = false; + IMenuCommandService mcs = MenuService; + if (mcs != null) + { + Capture = false; + Cursor.Clip = Rectangle.Empty; + ISelectionService s = (ISelectionService)GetService(typeof(ISelectionService)); + if (useSelection && s != null && !(1 == s.SelectionCount && s.PrimarySelection == mainDesigner.Component)) + { + mcs.ShowContextMenu(MenuCommands.TraySelectionMenu, x, y); + } + else + { + mcs.ShowContextMenu(MenuCommands.ComponentTrayMenu, x, y); + } + } + } } + void ISelectionUIHandler.OleDragEnter(DragEventArgs de) => GetOleDragHandler().DoOleDragEnter(de); + + void ISelectionUIHandler.OleDragDrop(DragEventArgs de) => GetOleDragHandler().DoOleDragDrop(de); + + void ISelectionUIHandler.OleDragOver(DragEventArgs de) => GetOleDragHandler().DoOleDragOver(de); + + void ISelectionUIHandler.OleDragLeave() => GetOleDragHandler().DoOleDragLeave(); + /// - /// Adds a component to the tray. + /// Adds a component to the tray. /// public virtual void AddComponent(IComponent component) { - throw new NotImplementedException(SR.NotImplementedByDesign); + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + // Ignore components that cannot be added to the tray + if (!CanDisplayComponent(component)) + { + return; + } + // And designate us as the selection UI handler for the control. + if (selectionUISvc == null) + { + selectionUISvc = (ISelectionUIService)GetService(typeof(ISelectionUIService)); + + // If there is no selection service, then we will provide our own. + if (selectionUISvc == null) + { + selectionUISvc = new SelectionUIService(host); + host.AddService(typeof(ISelectionUIService), selectionUISvc); + } + grabHandle = selectionUISvc.GetAdornmentDimensions(AdornmentType.GrabHandle); + } + + // Create a new instance of a tray control. + TrayControl trayctl = new TrayControl(this, component); + SuspendLayout(); + try + { + // Add it to us. + Controls.Add(trayctl); + controls.Add(trayctl); + // CanExtend can actually be called BEFORE the component is added to the ComponentTray. ToolStrip is such as scenario: + // 1. Add a timer to the Tray. + // 2. Add a ToolStrip. + // 3. ToolStripDesigner.Initialize will be called before ComponentTray.AddComponent, so the ToolStrip is not yet added to the tray. + // 4. TooStripDesigner.Initialize calls GetProperties, which causes our CanExtend to be called. + // 5. CanExtend will return false, since the component has not yet been added. + // 6. This causes all sorts of badness + // Fix is to refresh. + TypeDescriptor.Refresh(component); + if (host != null && !host.Loading) + { + PositionControl(trayctl); + } + if (selectionUISvc != null) + { + selectionUISvc.AssignSelectionUIHandler(component, this); + } + InheritanceAttribute attr = trayctl.InheritanceAttribute; + if (attr.InheritanceLevel != InheritanceLevel.NotInherited) + { + InheritanceUI iui = InheritanceUI; + if (iui != null) + { + iui.AddInheritedControl(trayctl, attr.InheritanceLevel); + } + } + } + finally + { + ResumeLayout(); + } + if (host != null && !host.Loading) + { + ScrollControlIntoView(trayctl); + } } [CLSCompliant(false)] protected virtual bool CanCreateComponentFromTool(ToolboxItem tool) { - throw new NotImplementedException(SR.NotImplementedByDesign); + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(host != null, "Service object could not provide us with a designer host."); + // Disallow controls to be added to the component tray. + Type compType = host.GetType(tool.TypeName); + if (compType == null) + return true; + if (!compType.IsSubclassOf(typeof(Control))) + { + return true; + } + Type designerType = GetDesignerType(compType, typeof(IDesigner)); + return ! (typeof(ControlDesigner).IsAssignableFrom(designerType)); + } + + private Type GetDesignerType(Type t, Type designerBaseType) + { + Type designerType = null; + // Get the set of attributes for this type + AttributeCollection attributes = TypeDescriptor.GetAttributes(t); + for (int i = 0; i < attributes.Count; i++) + { + if (attributes[i] is DesignerAttribute da) + { + Type attributeBaseType = Type.GetType(da.DesignerBaseTypeName); + if (attributeBaseType != null && attributeBaseType == designerBaseType) + { + bool foundService = false; + ITypeResolutionService tr = (ITypeResolutionService)GetService(typeof(ITypeResolutionService)); + if (tr != null) + { + foundService = true; + designerType = tr.GetType(da.DesignerTypeName); + } + + if (!foundService) + { + designerType = Type.GetType(da.DesignerTypeName); + } + + if (designerType != null) + { + break; + } + } + } + } + return designerType; } /// - /// This method determines if a UI representation for the given component should be provided. - /// If it returns true, then the component will get a glyph in the tray area. If it returns - /// false, then the component will not actually be added to the tray. The default - /// implementation looks for DesignTimeVisibleAttribute.Yes on the component's class. + /// This method determines if a UI representation for the given component should be provided. + /// If it returns true, then the component will get a glyph in the tray area. If it returns + /// false, then the component will not actually be added to the tray. The default + /// implementation looks for DesignTimeVisibleAttribute.Yes on the component's class. /// protected virtual bool CanDisplayComponent(IComponent component) { - throw new NotImplementedException(SR.NotImplementedByDesign); + return TypeDescriptor.GetAttributes(component).Contains(DesignTimeVisibleAttribute.Yes); } [CLSCompliant(false)] public void CreateComponentFromTool(ToolboxItem tool) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (!CanCreateComponentFromTool(tool)) + { + return; + } + // We invoke the drag drop handler for this. This implementation is shared between all designers that create components. + GetOleDragHandler().CreateTool(tool, null, 0, 0, 0, 0, false, false); } /// - /// Displays the given exception to the user. + /// Displays the given exception to the user. /// protected void DisplayError(Exception e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + IUIService uis = (IUIService)GetService(typeof(IUIService)); + if (uis != null) + { + uis.ShowError(e); + } + else + { + string message = e.Message; + if (message == null || message.Length == 0) + { + message = e.ToString(); + } + RTLAwareMessageBox.Show(null, message, null, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1, 0); + } } - // - /// - /// - /// Disposes of the resources (other than memory) used by the component tray object. - /// + /// + /// Disposes of the resources (other than memory) used by the component tray object. + /// /// protected override void Dispose(bool disposing) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (disposing && controls != null) + { + IExtenderProviderService es = (IExtenderProviderService)GetService(typeof(IExtenderProviderService)); + if (CompModSwitches.CommonDesignerServices.Enabled) + Debug.Assert(es != null, "IExtenderProviderService not found"); + if (es != null) + { + es.RemoveExtenderProvider(this); + } + + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (eventHandlerService != null) + { + if (host != null) + { + host.RemoveService(typeof(IEventHandlerService)); + eventHandlerService = null; + } + } + + IComponentChangeService componentChangeService = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (componentChangeService != null) + { + componentChangeService.ComponentRemoved -= new ComponentEventHandler(OnComponentRemoved); + } + + TypeDescriptor.Refreshed -= new RefreshEventHandler(OnComponentRefresh); + SystemEvents.DisplaySettingsChanged -= new EventHandler(OnSystemSettingChanged); + SystemEvents.InstalledFontsChanged -= new EventHandler(OnSystemSettingChanged); + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + IMenuCommandService mcs = MenuService; + if (mcs != null) + { + Debug.Assert(menucmdArrangeIcons != null, "Null Menu Command for ArrangeIcons"); + Debug.Assert(menucmdLineupIcons != null, "Null Menu Command for LineupIcons"); + Debug.Assert(menucmdLargeIcons != null, "Null Menu Command for LargeIcons"); + mcs.RemoveCommand(menucmdArrangeIcons); + mcs.RemoveCommand(menucmdLineupIcons); + mcs.RemoveCommand(menucmdLargeIcons); + } + + if (privateCommandSet != null) + { + privateCommandSet.Dispose(); + // If we created a private command set, we also added a selection ui service to the host + if (host != null) + { + host.RemoveService(typeof(ISelectionUIService)); + } + } + selectionUISvc = null; + + if (inheritanceUI != null) + { + inheritanceUI.Dispose(); + inheritanceUI = null; + } + + serviceProvider = null; + controls.Clear(); + controls = null; + + if (glyphManager != null) + { + glyphManager.Dispose(); + glyphManager = null; + } + } + base.Dispose(disposing); } /// - /// Similar to GetNextControl on Control, this method returns the next - /// component in the tray, given a starting component. It will return - /// null if the end (or beginning, if forward is false) of the list - /// is encountered. + /// Similar to GetNextControl on Control, this method returns the next + /// component in the tray, given a starting component. It will return + /// null if the end (or beginning, if forward is false) of the list + /// is encountered. /// public IComponent GetNextComponent(IComponent component, bool forward) { - throw new NotImplementedException(SR.NotImplementedByDesign); + for (int i = 0; i < controls.Count; i++) + { + TrayControl control = (TrayControl)controls[i]; + if (control.Component == component) + { + int targetIndex = (forward ? i + 1 : i - 1); + if (targetIndex >= 0 && targetIndex < controls.Count) + { + return ((TrayControl)controls[targetIndex]).Component; + } + // Reached the end of the road. + return null; + } + } + // If we got here then the component isn't in our list. Prime the caller with either the first or the last. + if (controls.Count > 0) + { + int targetIndex = (forward ? 0 : controls.Count - 1); + return ((TrayControl)controls[targetIndex]).Component; + } + return null; } /// - /// Accessor method for the location extender property. We offer this extender - /// to all non-visual components. + /// Accessor method for the location extender property. We offer this extender + /// to all non-visual components. /// [Category("Layout")] [Localizable(false)] @@ -227,12 +957,22 @@ public IComponent GetNextComponent(IComponent component, bool forward) [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] public Point GetLocation(IComponent receiver) { - throw new NotImplementedException(SR.NotImplementedByDesign); + PropertyDescriptor loc = TypeDescriptor.GetProperties(receiver.GetType())["Location"]; + if (loc != null) + { + // In this case the component already had a Location property, and what the caller wants is the underlying components Location, not the tray location. Why? Because we now use TrayLocation. + return (Point)(loc.GetValue(receiver)); + } + else + { + // If the component didn't already have a Location property, then the caller really wants the tray location. Could be a 3rd party vendor. + return GetTrayLocation(receiver); + } } /// - /// Accessor method for the location extender property. We offer this extender - /// to all non-visual components. + /// Accessor method for the location extender property. We offer this extender + /// to all non-visual components. /// [Category("Layout")] [Localizable(false)] @@ -241,166 +981,1991 @@ public Point GetLocation(IComponent receiver) [DesignOnly(true)] public Point GetTrayLocation(IComponent receiver) { - throw new NotImplementedException(SR.NotImplementedByDesign); + Control c = TrayControl.FromComponent(receiver); + if (c == null) + { + Debug.Fail("Anything we're extending should have a component view."); + return new Point(); + } + Point loc = c.Location; + Point autoScrollLoc = AutoScrollPosition; + return new Point(loc.X - autoScrollLoc.X, loc.Y - autoScrollLoc.Y); } /// - /// - /// Gets the requsted service type. - /// + /// + /// Gets the requsted service type. + /// /// protected override object GetService(Type serviceType) { - throw new NotImplementedException(SR.NotImplementedByDesign); + object service = null; + Debug.Assert(serviceProvider != null, "Trying to access services too late or too early."); + if (serviceProvider != null) + { + service = serviceProvider.GetService(serviceType); + } + return service; } /// - /// Returns true if the given componenent is being shown on the tray. + /// Returns true if the given componenent is being shown on the tray. /// public bool IsTrayComponent(IComponent comp) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (TrayControl.FromComponent(comp) == null) + { + return false; + } + foreach (Control control in Controls) + { + if (control is TrayControl tc && tc.Component == comp) + { + return true; + } + } + return false; } protected override void OnMouseDoubleClick(MouseEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + //give our glyphs first chance at this + if (glyphManager != null && glyphManager.OnMouseDoubleClick(e)) + { + //handled by a glyph - so don't send to the comp tray + return; + } + base.OnDoubleClick(e); + if (!TabOrderActive) + { + OnLostCapture(); + IEventBindingService eps = (IEventBindingService)GetService(typeof(IEventBindingService)); + if (CompModSwitches.CommonDesignerServices.Enabled) + Debug.Assert(eps != null, "IEventBindingService not found"); + if (eps != null) + { + eps.ShowCode(); + } + } } /// - /// Inheriting classes should override this method to handle this event. - /// Call base.onGiveFeedback to send this event to any registered event listeners. + /// Inheriting classes should override this method to handle this event. + /// Call base.onGiveFeedback to send this event to any registered event listeners. /// protected override void OnGiveFeedback(GiveFeedbackEventArgs gfevent) { - throw new NotImplementedException(SR.NotImplementedByDesign); + base.OnGiveFeedback(gfevent); + GetOleDragHandler().DoOleGiveFeedback(gfevent); } /// - /// Called in response to a drag drop for OLE drag and drop. Here we - /// drop a toolbox component on our parent control. + /// Called in response to a drag drop for OLE drag and drop. Here we + /// drop a toolbox component on our parent control. /// protected override void OnDragDrop(DragEventArgs de) { - throw new NotImplementedException(SR.NotImplementedByDesign); + // This will be used once during PositionComponent to place the component at the drop point. It is automatically set to null afterwards, so further components appear after the first one dropped. + mouseDropLocation = PointToClient(new Point(de.X, de.Y)); + autoScrollPosBeforeDragging = AutoScrollPosition; // save the scroll position + if (mouseDragTool != null) + { + ToolboxItem tool = mouseDragTool; + mouseDragTool = null; + if (CompModSwitches.CommonDesignerServices.Enabled) + Debug.Assert(GetService(typeof(IDesignerHost)) != null, "IDesignerHost not found"); + try + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + IDesigner designer = host.GetDesigner(host.RootComponent); + if (designer is IToolboxUser itu) + { + itu.ToolPicked(tool); + } + else + { + CreateComponentFromTool(tool); + } + } + catch (Exception e) + { + DisplayError(e); + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + } + de.Effect = DragDropEffects.Copy; + } + else + { + GetOleDragHandler().DoOleDragDrop(de); + } + mouseDropLocation = InvalidPoint; + ResumeLayout(); } /// - /// Called in response to a drag enter for OLE drag and drop. + /// Called in response to a drag enter for OLE drag and drop. /// protected override void OnDragEnter(DragEventArgs de) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (!TabOrderActive) + { + SuspendLayout(); + if (toolboxService == null) + { + toolboxService = (IToolboxService)GetService(typeof(IToolboxService)); + } + OleDragDropHandler dragDropHandler = GetOleDragHandler(); + object[] dragComps = dragDropHandler.GetDraggingObjects(de); + // Only assume the items came from the ToolBox if dragComps == null + if (toolboxService != null && dragComps == null) + { + mouseDragTool = toolboxService.DeserializeToolboxItem(de.Data, (IDesignerHost)GetService(typeof(IDesignerHost))); + } + if (mouseDragTool != null) + { + Debug.Assert(0 != (int)(de.AllowedEffect & (DragDropEffects.Move | DragDropEffects.Copy)), "DragDropEffect.Move | .Copy isn't allowed?"); + if ((int)(de.AllowedEffect & DragDropEffects.Move) != 0) + { + de.Effect = DragDropEffects.Move; + } + else + { + de.Effect = DragDropEffects.Copy; + } + } + else + { + dragDropHandler.DoOleDragEnter(de); + } + } } /// - /// Called when a drag-drop operation leaves the control designer view + /// Called when a drag-drop operation leaves the control designer view /// protected override void OnDragLeave(EventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + mouseDragTool = null; + GetOleDragHandler().DoOleDragLeave(); + ResumeLayout(); } /// - /// Called when a drag drop object is dragged over the control designer view + /// Called when a drag drop object is dragged over the control designer view /// protected override void OnDragOver(DragEventArgs de) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (mouseDragTool != null) + { + Debug.Assert(0 != (int)(de.AllowedEffect & DragDropEffects.Copy), "DragDropEffect.Move isn't allowed?"); + de.Effect = DragDropEffects.Copy; + } + else + { + GetOleDragHandler().DoOleDragOver(de); + } } /// /// - /// Forces the layout of any docked or anchored child controls. + /// Forces the layout of any docked or anchored child controls. /// protected override void OnLayout(LayoutEventArgs levent) { - throw new NotImplementedException(SR.NotImplementedByDesign); + DoAutoArrange(false); + // make sure selection service redraws + Invalidate(true); + base.OnLayout(levent); } /// - /// This is called when we lose capture. Here we get rid of any - /// rubber band we were drawing. You should put any cleanup - /// code in here. + /// This is called when we lose capture. Here we get rid of any + /// rubber band we were drawing. You should put any cleanup + /// code in here. /// protected virtual void OnLostCapture() { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (mouseDragStart != InvalidPoint) + { + Cursor.Clip = Rectangle.Empty; + if (mouseDragEnd != InvalidPoint) + { + DrawRubber(mouseDragStart, mouseDragEnd); + mouseDragEnd = InvalidPoint; + } + mouseDragStart = InvalidPoint; + } + } + + private void DrawRubber(Point start, Point end) + { + mouseDragWorkspace.X = Math.Min(start.X, end.X); + mouseDragWorkspace.Y = Math.Min(start.Y, end.Y); + mouseDragWorkspace.Width = Math.Abs(end.X - start.X); + mouseDragWorkspace.Height = Math.Abs(end.Y - start.Y); + mouseDragWorkspace = RectangleToScreen(mouseDragWorkspace); + ControlPaint.DrawReversibleFrame(mouseDragWorkspace, BackColor, FrameStyle.Dashed); } /// - /// Inheriting classes should override this method to handle this event. - /// Call base.onMouseDown to send this event to any registered event listeners. + /// Inheriting classes should override this method to handle this event. + /// Call base.onMouseDown to send this event to any registered event listeners. /// protected override void OnMouseDown(MouseEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + //give our glyphs first chance at this + if (glyphManager != null && glyphManager.OnMouseDown(e)) + { + //handled by a glyph - so don't send to the comp tray + return; + } + + base.OnMouseDown(e); + if (!TabOrderActive) + { + if (toolboxService == null) + { + toolboxService = (IToolboxService)GetService(typeof(IToolboxService)); + } + FocusDesigner(); + if (e.Button == MouseButtons.Left && toolboxService != null) + { + ToolboxItem tool = toolboxService.GetSelectedToolboxItem((IDesignerHost)GetService(typeof(IDesignerHost))); + if (tool != null) + { + // mouseDropLocation is checked in PositionControl, which should get called as a result of adding a new component. This allows us to set the position without flickering, while still providing support for auto layout if the control was double clicked or added through extensibility. + mouseDropLocation = new Point(e.X, e.Y); + try + { + CreateComponentFromTool(tool); + toolboxService.SelectedToolboxItemUsed(); + } + catch (Exception ex) + { + DisplayError(ex); + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + mouseDropLocation = InvalidPoint; + return; + } + } + + // If it is the left button, start a rubber band drag to laso controls. + if (e.Button == MouseButtons.Left) + { + mouseDragStart = new Point(e.X, e.Y); + Capture = true; + Cursor.Clip = RectangleToScreen(ClientRectangle); + } + else + { + try + { + ISelectionService ss = (ISelectionService)GetService(typeof(ISelectionService)); + if (CompModSwitches.CommonDesignerServices.Enabled) + Debug.Assert(ss != null, "ISelectionService not found"); + if (ss != null) + { + ss.SetSelectedComponents(new object[] { mainDesigner.Component }); + } + } + catch (Exception ex) + { + // nothing we can really do here; just eat it. + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + + } + } } /// - /// Inheriting classes should override this method to handle this event. - /// Call base.onMouseMove to send this event to any registered event listeners. + /// Inheriting classes should override this method to handle this event. + /// Call base.onMouseMove to send this event to any registered event listeners. /// protected override void OnMouseMove(MouseEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + //give our glyphs first chance at this + if (glyphManager != null && glyphManager.OnMouseMove(e)) + { + //handled by a glyph - so don't send to the comp tray + return; + } + base.OnMouseMove(e); + + // If we are dragging, then draw our little rubber band. + if (mouseDragStart != InvalidPoint) + { + if (mouseDragEnd != InvalidPoint) + { + DrawRubber(mouseDragStart, mouseDragEnd); + } + else + { + mouseDragEnd = new Point(0, 0); + } + mouseDragEnd.X = e.X; + mouseDragEnd.Y = e.Y; + DrawRubber(mouseDragStart, mouseDragEnd); + } } /// - /// Inheriting classes should override this method to handle this event. - /// Call base.onMouseUp to send this event to any registered event listeners. + /// Inheriting classes should override this method to handle this event. + /// Call base.onMouseUp to send this event to any registered event listeners. /// protected override void OnMouseUp(MouseEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + //give our glyphs first chance at this + if (glyphManager != null && glyphManager.OnMouseUp(e)) + { + //handled by a glyph - so don't send to the comp tray + return; + } + + if (mouseDragStart != InvalidPoint && e.Button == MouseButtons.Left) + { + object[] comps; + Capture = false; + Cursor.Clip = Rectangle.Empty; + if (mouseDragEnd != InvalidPoint) + { + DrawRubber(mouseDragStart, mouseDragEnd); + Rectangle rect = new Rectangle + { + X = Math.Min(mouseDragStart.X, e.X), + Y = Math.Min(mouseDragStart.Y, e.Y), + Width = Math.Abs(e.X - mouseDragStart.X), + Height = Math.Abs(e.Y - mouseDragStart.Y) + }; + comps = GetComponentsInRect(rect); + mouseDragEnd = InvalidPoint; + } + else + { + comps = new object[0]; + } + + if (comps.Length == 0) + { + comps = new object[] { mainDesigner.Component }; + } + try + { + ISelectionService ss = (ISelectionService)GetService(typeof(ISelectionService)); + if (CompModSwitches.CommonDesignerServices.Enabled) + Debug.Assert(ss != null, "ISelectionService not found"); + if (ss != null) + { + ss.SetSelectedComponents(comps); + } + } + catch (Exception ex) + { + // nothing we can really do here; just eat it. + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + mouseDragStart = InvalidPoint; + } + base.OnMouseUp(e); + } + + private object[] GetComponentsInRect(Rectangle rect) + { + ArrayList list = new ArrayList(); + int controlCount = Controls.Count; + for (int i = 0; i < controlCount; i++) + { + Control child = Controls[i]; + Rectangle bounds = child.Bounds; + if (child is TrayControl tc && bounds.IntersectsWith(rect)) + { + list.Add(tc.Component); + } + } + return list.ToArray(); } protected override void OnPaint(PaintEventArgs pe) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (fResetAmbient || fSelectionChanged) + { + fResetAmbient = false; + fSelectionChanged = false; + IUIService uiService = (IUIService)GetService(typeof(IUIService)); + if (uiService != null) + { + Color styleColor; + if (uiService.Styles["ArtboardBackground"] is Color) + { + styleColor = (Color)uiService.Styles["ArtboardBackground"]; + } + //Can't use 'as' here since Color is a value type + else if (uiService.Styles["VsColorDesignerTray"] is Color) + { + styleColor = (Color)uiService.Styles["VsColorDesignerTray"]; + } + else if (uiService.Styles["HighlightColor"] is Color) + { + // Since v1, we have had code here that checks for HighlightColor, so some hosts (like WinRes) have been setting it. If VsColorDesignerTray isn't present, we look for HighlightColor for backward compat. + styleColor = (Color)uiService.Styles["HighlightColor"]; + } + else + { + //No style color provided? Let's pick a default. + styleColor = SystemColors.Info; + } + + BackColor = styleColor; + Font = (Font)uiService.Styles["DialogFont"]; + foreach (Control ctl in controls) + { + ctl.BackColor = styleColor; + ctl.ForeColor = ForeColor; + } + } + } + + base.OnPaint(pe); + Graphics gr = pe.Graphics; + // Now, if we have a selection, paint it + if (selectedObjects != null) + { + bool first = true;//indicates the first iteration of our foreach loop + HatchBrush selectionBorderBrush; + if (SystemInformation.HighContrast) + { + selectionBorderBrush = new HatchBrush(HatchStyle.Percent50, SystemColors.HighlightText, Color.Transparent); + } + else + { + selectionBorderBrush = new HatchBrush(HatchStyle.Percent50, SystemColors.ControlDarkDark, Color.Transparent); + } + + try + { + foreach (object o in selectedObjects) + { + Control c = ((IOleDragClient)this).GetControlForComponent(o); + if (c != null && c.Visible) + { + Rectangle innerRect = c.Bounds; + if (SystemInformation.HighContrast) + { + c.ForeColor = SystemColors.HighlightText; + c.BackColor = SystemColors.Highlight; + } + NoResizeHandleGlyph glyph = new NoResizeHandleGlyph(innerRect, SelectionRules.None, first, null); + gr.FillRectangle(selectionBorderBrush, DesignerUtils.GetBoundsForNoResizeSelectionType(innerRect, SelectionBorderGlyphType.Top)); + gr.FillRectangle(selectionBorderBrush, DesignerUtils.GetBoundsForNoResizeSelectionType(innerRect, SelectionBorderGlyphType.Bottom)); + gr.FillRectangle(selectionBorderBrush, DesignerUtils.GetBoundsForNoResizeSelectionType(innerRect, SelectionBorderGlyphType.Left)); + gr.FillRectangle(selectionBorderBrush, DesignerUtils.GetBoundsForNoResizeSelectionType(innerRect, SelectionBorderGlyphType.Right)); + // Need to draw this one last + DesignerUtils.DrawNoResizeHandle(gr, glyph.Bounds, first, glyph); + } + first = false; + } + } + finally + { + if (selectionBorderBrush != null) + { + selectionBorderBrush.Dispose(); + } + } + } + //paint any glyphs + if (glyphManager != null) + { + glyphManager.OnPaintGlyphs(pe); + } } /// - /// Sets the cursor. You may override this to set your own - /// cursor. + /// Sets the cursor. You may override this to set your own + /// cursor. /// protected virtual void OnSetCursor() { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (toolboxService == null) + { + toolboxService = (IToolboxService)GetService(typeof(IToolboxService)); + } + if (toolboxService == null || !toolboxService.SetCursor()) + { + Cursor.Current = Cursors.Default; + } } /// - /// Removes a component from the tray. + /// Removes a component from the tray. /// public virtual void RemoveComponent(IComponent component) { - throw new NotImplementedException(SR.NotImplementedByDesign); + TrayControl c = TrayControl.FromComponent(component); + if (c != null) + { + try + { + InheritanceAttribute attr = c.InheritanceAttribute; + if (attr.InheritanceLevel != InheritanceLevel.NotInherited && inheritanceUI != null) + { + inheritanceUI.RemoveInheritedControl(c); + } + if (controls != null) + { + int index = controls.IndexOf(c); + if (index != -1) + controls.RemoveAt(index); + } + } + finally + { + c.Dispose(); + } + } } /// - /// Accessor method for the location extender property. We offer this extender - /// to all non-visual components. + /// Accessor method for the location extender property. We offer this extender + /// to all non-visual components. /// public void SetLocation(IComponent receiver, Point location) { - throw new NotImplementedException(SR.NotImplementedByDesign); + // This really should only be called when we are loading. + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null && host.Loading) + { + // If we are loading, and we get called here, that's because we have provided the extended Location property. In this case we are loading an old project, and what we are really setting is the tray location. + SetTrayLocation(receiver, location); + } + else + { + // we are not loading + PropertyDescriptor loc = TypeDescriptor.GetProperties(receiver.GetType())["Location"]; + if (loc != null) + { + // so if the component already had the Location property, what the caller wants is really the underlying component's Location property. + loc.SetValue(receiver, location); + } + else + { + // if the component didn't have a Location property, then the caller really wanted the tray location. + SetTrayLocation(receiver, location); + } + } } /// - /// Accessor method for the location extender property. We offer this extender - /// to all non-visual components. + /// Accessor method for the location extender property. We offer this extender + /// to all non-visual components. /// public void SetTrayLocation(IComponent receiver, Point location) { - throw new NotImplementedException(SR.NotImplementedByDesign); + TrayControl c = TrayControl.FromComponent(receiver); + if (c == null) + { + Debug.Fail("Anything we're extending should have a component view."); + return; + } + if (c.Parent == this) + { + Point autoScrollLoc = AutoScrollPosition; + location = new Point(location.X + autoScrollLoc.X, location.Y + autoScrollLoc.Y); + if (c.Visible) + { + RearrangeInAutoSlots(c, location); + } + } + else if (!c.Location.Equals(location)) + { + c.Location = location; + c.Positioned = true; + } } /// - /// We override our base class's WndProc to monitor certain messages. + /// We override our base class's WndProc to monitor certain messages. /// protected override void WndProc(ref Message m) { - throw new NotImplementedException(SR.NotImplementedByDesign); + switch (m.Msg) + { + case NativeMethods.WM_CANCELMODE: + // When we get cancelmode (i.e. you tabbed away to another window) then we want to cancel any pending drag operation! + OnLostCapture(); + break; + case NativeMethods.WM_SETCURSOR: + OnSetCursor(); + return; + case NativeMethods.WM_HSCROLL: + case NativeMethods.WM_VSCROLL: + // When we scroll, we reposition a control without causing a property change event. Therefore, we must tell the selection UI service to sync itself. + base.WndProc(ref m); + if (selectionUISvc != null) + { + selectionUISvc.SyncSelection(); + } + return; + case NativeMethods.WM_STYLECHANGED: + // When the scroll bars first appear, we need to invalidate so we properly paint our grid. + Invalidate(); + break; + case NativeMethods.WM_CONTEXTMENU: + // Pop a context menu for the composition designer. + int x = NativeMethods.Util.SignedLOWORD(unchecked((int)(long)m.LParam)); + int y = NativeMethods.Util.SignedHIWORD(unchecked((int)(long)m.LParam)); + if (x == -1 && y == -1) + { + // for shift-F10 + Point mouse = Control.MousePosition; + x = mouse.X; + y = mouse.Y; + } + OnContextMenu(x, y, true); + break; + case NativeMethods.WM_NCHITTEST: + if (glyphManager != null) + { + // Get a hit test on any glyhs that we are managing this way - we know where to route appropriate messages + Point pt = new Point((short)NativeMethods.Util.LOWORD(unchecked((int)(long)m.LParam)), (short)NativeMethods.Util.HIWORD(unchecked((int)(long)m.LParam))); + NativeMethods.POINT pt1 = new NativeMethods.POINT + { + x = 0, + y = 0 + }; + NativeMethods.MapWindowPoints(IntPtr.Zero, Handle, pt1, 1); + pt.Offset(pt1.x, pt1.y); + glyphManager.GetHitTest(pt); + } + base.WndProc(ref m); + break; + default: + base.WndProc(ref m); + break; + } + } + + internal TrayControl GetTrayControlFromComponent(IComponent comp) + { + return TrayControl.FromComponent(comp); + } + + private bool TabOrderActive + { + get + { + if (!queriedTabOrder) + { + queriedTabOrder = true; + IMenuCommandService mcs = MenuService; + if (mcs != null) + { + tabOrderCommand = mcs.FindCommand(MenuCommands.TabOrder); + } + } + if (tabOrderCommand != null) + { + return tabOrderCommand.Checked; + } + return false; + } + } + + private InheritanceUI InheritanceUI + { + get + { + if (inheritanceUI == null) + { + inheritanceUI = new InheritanceUI(); + } + return inheritanceUI; + } + } + + private IMenuCommandService MenuService + { + get + { + if (menuCommandService == null) + { + menuCommandService = (IMenuCommandService)GetService(typeof(IMenuCommandService)); + } + return menuCommandService; + } + } + + internal void FocusDesigner() + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null && host.RootComponent != null) + { + if (host.GetDesigner(host.RootComponent) is IRootDesigner rd) + { + ViewTechnology[] techs = rd.SupportedTechnologies; + if (techs.Length > 0) + { + if (rd.GetView(techs[0]) is Control view) + { + view.Focus(); + } + } + } + } + } + + internal Size ParentGridSize + { + get + { + if (mainDesigner is ParentControlDesigner designer) + { + return designer.ParentGridSize; + } + + return new Size(8, 8); + } + } + + internal void UpdatePastePositions(ArrayList components) + { + foreach (TrayControl c in components) + { + if (!CanDisplayComponent(c.Component)) + { + return; + } + + if (mouseDropLocation == InvalidPoint) + { + Control prevCtl = null; + if (controls.Count > 1) + { + prevCtl = (Control)controls[controls.Count - 1]; + } + PositionInNextAutoSlot(c, prevCtl, true); + } + else + { + PositionControl(c); + } + c.BringToFront(); + } + } + + private void PositionControl(TrayControl c) + { + Debug.Assert(c.Visible, "TrayControl for " + c.Component + " should not be positioned"); + if (!autoArrange) + { + if (mouseDropLocation != InvalidPoint) + { + if (!c.Location.Equals(mouseDropLocation)) + c.Location = mouseDropLocation; + } + else + { + Control prevCtl = null; + if (controls.Count > 1) + { + // PositionControl can be called when all the controls have been added (from IOleDragClient.AddComponent), so we can't use the old way of looking up the previous control (prevCtl = controls[controls.Count - 2] + int index = controls.IndexOf(c); + Debug.Assert(index >= 1, "Got the wrong index, how could that be?"); + if (index >= 1) + { + prevCtl = (Control)controls[index - 1]; + } + } + PositionInNextAutoSlot(c, prevCtl, true); + } + } + else + { + if (mouseDropLocation != InvalidPoint) + { + RearrangeInAutoSlots(c, mouseDropLocation); + } + else + { + Control prevCtl = null; + if (controls.Count > 1) + { + int index = controls.IndexOf(c); + Debug.Assert(index >= 1, "Got the wrong index, how could that be?"); + if (index >= 1) + { + prevCtl = (Control)controls[index - 1]; + } + } + PositionInNextAutoSlot(c, prevCtl, true); + } + } + } + + internal void RearrangeInAutoSlots(Control c, Point pos) + { +#if DEBUG + int index = controls.IndexOf(c); + Debug.Assert(index != -1, "Add control to the list of controls before autoarranging.!!!"); + Debug.Assert(Visible == c.Visible, "TrayControl for " + ((TrayControl)c).Component + " should not be positioned"); +#endif + TrayControl tc = (TrayControl)c; + tc.Positioned = true; + tc.Location = pos; + } + + private bool PositionInNextAutoSlot(TrayControl c, Control prevCtl, bool dirtyDesigner) + { + Debug.Assert(c.Visible, "TrayControl for " + c.Component + " should not be positioned"); + if (whiteSpace.IsEmpty) + { + Debug.Assert(selectionUISvc != null, "No SelectionUIService available for tray."); + whiteSpace = new Point(selectionUISvc.GetAdornmentDimensions(AdornmentType.GrabHandle)); + whiteSpace.X = whiteSpace.X * 2 + 3; + whiteSpace.Y = whiteSpace.Y * 2 + 3; + } + + if (prevCtl == null) + { + Rectangle display = DisplayRectangle; + Point newLoc = new Point(display.X + whiteSpace.X, display.Y + whiteSpace.Y); + if (!c.Location.Equals(newLoc)) + { + c.Location = newLoc; + if (dirtyDesigner) + { + IComponent comp = c.Component; + Debug.Assert(comp != null, "Component for the TrayControl is null"); + PropertyDescriptor ctlLocation = TypeDescriptor.GetProperties(comp)["TrayLocation"]; + if (ctlLocation != null) + { + Point autoScrollLoc = AutoScrollPosition; + newLoc = new Point(newLoc.X - autoScrollLoc.X, newLoc.Y - autoScrollLoc.Y); + ctlLocation.SetValue(comp, newLoc); + } + } + else + { + c.Location = newLoc; + } + return true; + } + } + else + { + // Calcuate the next location for this control. + Rectangle bounds = prevCtl.Bounds; + Point newLoc = new Point(bounds.X + bounds.Width + whiteSpace.X, bounds.Y); + + // Check to see if it goes over the edge of our window. If it does, then wrap it. + if (newLoc.X + c.Size.Width > Size.Width) + { + newLoc.X = whiteSpace.X; + newLoc.Y += bounds.Height + whiteSpace.Y; + } + + if (!c.Location.Equals(newLoc)) + { + if (dirtyDesigner) + { + IComponent comp = c.Component; + Debug.Assert(comp != null, "Component for the TrayControl is null"); + PropertyDescriptor ctlLocation = TypeDescriptor.GetProperties(comp)["TrayLocation"]; + if (ctlLocation != null) + { + Point autoScrollLoc = AutoScrollPosition; + newLoc = new Point(newLoc.X - autoScrollLoc.X, newLoc.Y - autoScrollLoc.Y); + ctlLocation.SetValue(comp, newLoc); + } + } + else + { + c.Location = newLoc; + } + return true; + } + } + return false; + } + + internal class TrayControl : Control + { + // Values that define this tray control + private readonly IComponent _component; // the component this control is representing + private Image _toolboxBitmap; // the bitmap used to represent the component + private int _cxIcon; // the dimensions of the bitmap + private int _cyIcon; // the dimensions of the bitmap + private readonly InheritanceAttribute _inheritanceAttribute; + + // Services that we use often enough to cache. + private readonly ComponentTray _tray; + // transient values that are used during mouse drags + private Point _mouseDragLast = InvalidPoint; // the last position of the mouse during a drag. + private bool _mouseDragMoved; // has the mouse been moved during this drag? + private bool _ctrlSelect = false; // was the ctrl key down on the mouse down? + private bool _positioned = false; // Have we given this control an explicit location yet? + private const int WhiteSpace = 5; + private readonly int _borderWidth; + internal bool _fRecompute = false; // This flag tells the TrayControl that it needs to retrieve the font and the background color before painting. + + /// + /// Creates a new TrayControl based on the component. + /// + public TrayControl(ComponentTray tray, IComponent component) + { + _tray = tray; + _component = component; + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + SetStyle(ControlStyles.Selectable, false); + _borderWidth = SystemInformation.BorderSize.Width; + UpdateIconInfo(); + + IComponentChangeService cs = (IComponentChangeService)tray.GetService(typeof(IComponentChangeService)); + if (CompModSwitches.CommonDesignerServices.Enabled) + Debug.Assert(cs != null, "IComponentChangeService not found"); + if (cs != null) + { + cs.ComponentRename += new ComponentRenameEventHandler(OnComponentRename); + } + + ISite site = component.Site; + string name = null; + + if (site != null) + { + name = site.Name; + IDictionaryService ds = (IDictionaryService)site.GetService(typeof(IDictionaryService)); + Debug.Assert(ds != null, "ComponentTray relies on IDictionaryService, which is not available."); + if (ds != null) + { + ds.SetValue(GetType(), this); + } + } + + if (name == null) + { + // We always want name to have something in it, so we default to the class name. This way the design instance contains something semi-intuitive if we don't have a site. + name = component.GetType().Name; + } + Text = name; + _inheritanceAttribute = (InheritanceAttribute)TypeDescriptor.GetAttributes(component)[typeof(InheritanceAttribute)]; + TabStop = false; + + + } + + /// + /// Retrieves the compnent this control is representing. + /// + public IComponent Component + { + get => _component; + } + + public override Font Font + { + get => _tray.Font; + } + + public InheritanceAttribute InheritanceAttribute + { + get => _inheritanceAttribute; + } + + public bool Positioned + { + get => _positioned; + set => _positioned = value; + } + + /// + /// Adjusts the size of the control based on the contents. + /// + // CONSIDER: this method gets called three or four times per component, and is even reentrant (CreateGraphics can force handle creation, and OnCreateHandle calls this method). There's probably a better way to do this, but since this doesn't seem to be on the critical path, I'm not going to lose sleep over it. + private void AdjustSize() + { + // CONSIDER: this forces handle creation. Can we delay this calculation? + Graphics gr = CreateGraphics(); + try + { + Size sz = Size.Ceiling(gr.MeasureString(Text, Font)); + + Rectangle rc = Bounds; + + if (_tray.ShowLargeIcons) + { + rc.Width = Math.Max(_cxIcon, sz.Width) + 4 * _borderWidth + 2 * WhiteSpace; + rc.Height = _cyIcon + 2 * WhiteSpace + sz.Height + 4 * _borderWidth; + } + else + { + rc.Width = _cxIcon + sz.Width + 4 * _borderWidth + 2 * WhiteSpace; + rc.Height = Math.Max(_cyIcon, sz.Height) + 4 * _borderWidth; + } + + Bounds = rc; + Invalidate(); + } + + finally + { + if (gr != null) + { + gr.Dispose(); + } + } + + if (_tray.glyphManager != null) + { + _tray.glyphManager.UpdateLocation(this); + } + } + + protected override AccessibleObject CreateAccessibilityInstance() => new TrayControlAccessibleObject(this, _tray); + + /// + /// Destroys this control. Views automatically destroy themselves when they are removed from the design container. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + ISite site = _component.Site; + if (site != null) + { + IComponentChangeService cs = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + if (CompModSwitches.CommonDesignerServices.Enabled) + Debug.Assert(cs != null, "IComponentChangeService not found"); + if (cs != null) + { + cs.ComponentRename -= new ComponentRenameEventHandler(OnComponentRename); + } + + IDictionaryService ds = (IDictionaryService)site.GetService(typeof(IDictionaryService)); + if (CompModSwitches.CommonDesignerServices.Enabled) + Debug.Assert(ds != null, "IDictionaryService not found"); + if (ds != null) + { + ds.SetValue(typeof(TrayControl), null); + } + } + } + base.Dispose(disposing); + } + + /// + /// Retrieves the tray control object for the given component. + /// + public static TrayControl FromComponent(IComponent component) + { + TrayControl c = null; + if (component == null) + { + return null; + } + + ISite site = component.Site; + if (site != null) + { + IDictionaryService ds = (IDictionaryService)site.GetService(typeof(IDictionaryService)); + if (CompModSwitches.CommonDesignerServices.Enabled) + Debug.Assert(ds != null, "IDictionaryService not found"); + if (ds != null) + { + c = (TrayControl)ds.GetValue(typeof(TrayControl)); + } + } + return c; + } + + /// + /// Delegate that is called in response to a name change. Here we update our own stashed version of the name, recalcuate our size and repaint. + /// + private void OnComponentRename(object sender, ComponentRenameEventArgs e) + { + if (e.Component == _component) + { + Text = e.NewName; + AdjustSize(); + } + } + + /// + /// Overrides handle creation notification for a control. Here we just ensure that we're the proper size. + /// + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + AdjustSize(); + } + + /// + /// Called in response to a double-click of the left mouse button. The default behavior here calls onDoubleClick on IMouseHandler + /// + protected override void OnDoubleClick(EventArgs e) + { + base.OnDoubleClick(e); + if (!_tray.TabOrderActive) + { + IDesignerHost host = (IDesignerHost)_tray.GetService(typeof(IDesignerHost)); + Debug.Assert(host != null, "Component tray does not have access to designer host."); + if (host != null) + { + _mouseDragLast = InvalidPoint; + Capture = false; + // We try to get a designer for the component and let it view the event. If this fails, then we'll try to do it ourselves. + IDesigner designer = host.GetDesigner(_component); + if (designer == null) + { + ViewDefaultEvent(_component); + } + else + { + designer.DoDefaultAction(); + } + } + } + } + + /// + /// Terminates our drag operation. + /// + private void OnEndDrag(bool cancel) + { + _mouseDragLast = InvalidPoint; + if (!_mouseDragMoved) + { + if (_ctrlSelect) + { + ISelectionService sel = (ISelectionService)_tray.GetService(typeof(ISelectionService)); + if (sel != null) + { + sel.SetSelectedComponents(new object[] { Component }, SelectionTypes.Primary); + } + _ctrlSelect = false; + } + return; + } + _mouseDragMoved = false; + _ctrlSelect = false; + Capture = false; + OnSetCursor(); + + // And now finish the drag. + Debug.Assert(_tray.selectionUISvc != null, "We shouldn't be able to begin a drag without this"); + if (_tray.selectionUISvc != null && _tray.selectionUISvc.Dragging) + { + _tray.selectionUISvc.EndDrag(cancel); + } + } + + /// + /// Called when the mouse button is pressed down. Here, we provide drag support for the component. + /// + protected override void OnMouseDown(MouseEventArgs me) + { + base.OnMouseDown(me); + if (!_tray.TabOrderActive) + { + _tray.FocusDesigner(); + // If this is the left mouse button, then begin a drag. + if (me.Button == MouseButtons.Left) + { + Capture = true; + _mouseDragLast = PointToScreen(new Point(me.X, me.Y)); + // If the CTRL key isn't down, select this component, otherwise, we wait until the mouse up. Make sure the component is selected + _ctrlSelect = NativeMethods.GetKeyState((int)Keys.ControlKey) != 0; + if (!_ctrlSelect) + { + ISelectionService sel = (ISelectionService)_tray.GetService(typeof(ISelectionService)); + // Make sure the component is selected + if (CompModSwitches.CommonDesignerServices.Enabled) + Debug.Assert(sel != null, "ISelectionService not found"); + if (sel != null) + { + sel.SetSelectedComponents(new object[] { Component }, SelectionTypes.Primary); + } + } + } + } + } + + /// + /// Called when the mouse is moved over the component. We update our drag information here if we're dragging the component around. + /// + protected override void OnMouseMove(MouseEventArgs me) + { + base.OnMouseMove(me); + if (_mouseDragLast == InvalidPoint) + { + return; + } + + if (!_mouseDragMoved) + { + Size minDrag = SystemInformation.DragSize; + Size minDblClick = SystemInformation.DoubleClickSize; + minDrag.Width = Math.Max(minDrag.Width, minDblClick.Width); + minDrag.Height = Math.Max(minDrag.Height, minDblClick.Height); + // we have to make sure the mouse moved farther than the minimum drag distance before we actually start the drag + Point newPt = PointToScreen(new Point(me.X, me.Y)); + if (_mouseDragLast == InvalidPoint || + (Math.Abs(_mouseDragLast.X - newPt.X) < minDrag.Width && + Math.Abs(_mouseDragLast.Y - newPt.Y) < minDrag.Height)) + { + return; + } + else + { + _mouseDragMoved = true; + // we're on the move, so we're not in a ctrlSelect + _ctrlSelect = false; + } + } + + try + { + // Make sure the component is selected + ISelectionService sel = (ISelectionService)_tray.GetService(typeof(ISelectionService)); + if (sel != null) + { + sel.SetSelectedComponents(new object[] { Component }, SelectionTypes.Primary); + } + + // Notify the selection service that all the components are in the "mouse down" mode. + if (_tray.selectionUISvc != null && _tray.selectionUISvc.BeginDrag(SelectionRules.Visible | SelectionRules.Moveable, _mouseDragLast.X, _mouseDragLast.Y)) + { + OnSetCursor(); + } + } + finally + { + _mouseDragMoved = false; + _mouseDragLast = InvalidPoint; + } + } + + /// + /// Called when the mouse button is released. Here, we finish our drag if one was started. + /// + protected override void OnMouseUp(MouseEventArgs me) + { + base.OnMouseUp(me); + OnEndDrag(false); + } + + /// + /// Called when we are to display our context menu for this component. + /// + private void OnContextMenu(int x, int y) + { + if (!_tray.TabOrderActive) + { + Capture = false; + // Ensure that this component is selected. + ISelectionService s = (ISelectionService)_tray.GetService(typeof(ISelectionService)); + if (s != null && !s.GetComponentSelected(_component)) + { + s.SetSelectedComponents(new object[] { _component }, SelectionTypes.Replace); + } + IMenuCommandService mcs = _tray.MenuService; + if (mcs != null) + { + Capture = false; + Cursor.Clip = Rectangle.Empty; + mcs.ShowContextMenu(MenuCommands.TraySelectionMenu, x, y); + } + } + } + + /// + /// Painting for our control. + /// + protected override void OnPaint(PaintEventArgs e) + { + if (_fRecompute) + { + _fRecompute = false; + UpdateIconInfo(); + } + base.OnPaint(e); + Rectangle rc = ClientRectangle; + rc.X += WhiteSpace + _borderWidth; + rc.Y += _borderWidth; + rc.Width -= (2 * _borderWidth + WhiteSpace); + rc.Height -= 2 * _borderWidth; + StringFormat format = new StringFormat(); + Brush foreBrush = new SolidBrush(ForeColor); + try + { + format.Alignment = StringAlignment.Center; + if (_tray.ShowLargeIcons) + { + if (null != _toolboxBitmap) + { + int x = rc.X + (rc.Width - _cxIcon) / 2; + int y = rc.Y + WhiteSpace; + e.Graphics.DrawImage(_toolboxBitmap, new Rectangle(x, y, _cxIcon, _cyIcon)); + } + + rc.Y += (_cyIcon + WhiteSpace); + rc.Height -= _cyIcon; + e.Graphics.DrawString(Text, Font, foreBrush, rc, format); + } + else + { + if (null != _toolboxBitmap) + { + int y = rc.Y + (rc.Height - _cyIcon) / 2; + e.Graphics.DrawImage(_toolboxBitmap, new Rectangle(rc.X, y, _cxIcon, _cyIcon)); + } + rc.X += (_cxIcon + _borderWidth); + rc.Width -= _cxIcon; + rc.Y += 3; + e.Graphics.DrawString(Text, Font, foreBrush, rc); + } + } + + finally + { + if (format != null) + { + format.Dispose(); + } + if (foreBrush != null) + { + foreBrush.Dispose(); + } + } + + // If this component is being inherited, paint it as such + if (!InheritanceAttribute.NotInherited.Equals(_inheritanceAttribute)) + { + InheritanceUI iui = _tray.InheritanceUI; + if (iui != null) + { + e.Graphics.DrawImage(iui.InheritanceGlyph, 0, 0); + } + } + } + + /// + /// Overrides control's FontChanged. Here we re-adjust our size if the font changes. + /// + protected override void OnFontChanged(EventArgs e) + { + AdjustSize(); + base.OnFontChanged(e); + } + + /// + /// Overrides control's LocationChanged. Here, we make sure that any glyphs associated with us are also relocated. + /// + protected override void OnLocationChanged(EventArgs e) + { + if (_tray.glyphManager != null) + { + _tray.glyphManager.UpdateLocation(this); + } + } + + /// + /// Overrides control's TextChanged. Here we re-adjust our size if the font changes. + /// + protected override void OnTextChanged(EventArgs e) + { + AdjustSize(); + base.OnTextChanged(e); + } + + /// + /// Called each time the cursor needs to be set. The ControlDesigner behavior here will set the cursor to one of three things: + /// 1. If the selection UI service shows a locked selection, or if there is no location property on the control, then the default arrow will be set. + /// 2. Otherwise, the four headed arrow will be set to indicate that the component can be clicked and moved. + /// 3. If the user is currently dragging a component, the crosshair cursor will be used instead of the four headed arrow. + /// + private void OnSetCursor() + { + // Check that the component is not locked. + PropertyDescriptor prop; + try + { + prop = TypeDescriptor.GetProperties(_component)["Locked"]; + } + catch (FileNotFoundException e) + { + // In case an unhandled exception was encountered, we don't want to leave the cursor with some strange shape Currently we have watson logs with FileNotFoundException only, so we are scoping the catch only to that type. + Cursor.Current = Cursors.Default; + Debug.Fail(e.Message); + return; + } + + if (prop != null && ((bool)prop.GetValue(_component)) == true) + { + Cursor.Current = Cursors.Default; + return; + } + + // Ask the tray to see if the tab order UI is not running. + if (_tray.TabOrderActive) + { + Cursor.Current = Cursors.Default; + return; + } + + if (_mouseDragMoved) + { + Cursor.Current = Cursors.Default; + } + else if (_mouseDragLast != InvalidPoint) + { + Cursor.Current = Cursors.Cross; + } + else + { + Cursor.Current = Cursors.SizeAll; + } + } + + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) + { + if (!_tray.AutoArrange || + (specified & BoundsSpecified.Width) == BoundsSpecified.Width || + (specified & BoundsSpecified.Height) == BoundsSpecified.Height) + { + + base.SetBoundsCore(x, y, width, height, specified); + } + Rectangle bounds = Bounds; + Size parentGridSize = _tray.ParentGridSize; + if (Math.Abs(bounds.X - x) > parentGridSize.Width || Math.Abs(bounds.Y - y) > parentGridSize.Height) + { + base.SetBoundsCore(x, y, width, height, specified); + } + + } + + protected override void SetVisibleCore(bool value) + { + if (value && !_tray.CanDisplayComponent(_component)) + return; + base.SetVisibleCore(value); + } + + public override string ToString() => "ComponentTray: " + _component.ToString(); + + internal void UpdateIconInfo() + { + ToolboxBitmapAttribute attr = (ToolboxBitmapAttribute)TypeDescriptor.GetAttributes(_component)[typeof(ToolboxBitmapAttribute)]; + if (attr != null) + { + _toolboxBitmap = attr.GetImage(_component, _tray.ShowLargeIcons); + } + + // Get the size of the bitmap so we can size our component correctly. + if (null == _toolboxBitmap) + { + _cxIcon = 0; + _cyIcon = SystemInformation.IconSize.Height; + } + else + { + Size sz = _toolboxBitmap.Size; + _cxIcon = sz.Width; + _cyIcon = sz.Height; + } + AdjustSize(); + } + + /// + /// This creates a method signature in the source code file for the default event on the component and navigates the user's cursor to that location. + /// + public virtual void ViewDefaultEvent(IComponent component) + { + EventDescriptor defaultEvent = TypeDescriptor.GetDefaultEvent(component); + PropertyDescriptor defaultPropEvent = null; + bool eventChanged = false; + IEventBindingService eps = (IEventBindingService)GetService(typeof(IEventBindingService)); + if (CompModSwitches.CommonDesignerServices.Enabled) + Debug.Assert(eps != null, "IEventBindingService not found"); + if (eps != null) + { + defaultPropEvent = eps.GetEventProperty(defaultEvent); + } + + // If we couldn't find a property for this event, or if the property is read only, then abort and just show the code. + if (defaultPropEvent == null || defaultPropEvent.IsReadOnly) + { + if (eps != null) + { + eps.ShowCode(); + } + return; + } + + string handler = (string)defaultPropEvent.GetValue(component); + + // If there is no handler set, set one now. + if (handler == null) + { + eventChanged = true; + handler = eps.CreateUniqueMethodName(component, defaultEvent); + } + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + DesignerTransaction trans = null; + + try + { + if (host != null) + { + trans = host.CreateTransaction(string.Format(SR.WindowsFormsAddEvent, defaultEvent.Name)); + } + // Save the new value... BEFORE navigating to it! + if (eventChanged && defaultPropEvent != null) + { + defaultPropEvent.SetValue(component, handler); + } + eps.ShowCode(component, defaultEvent); + } + finally + { + if (trans != null) + { + trans.Commit(); + } + } + } + + /// + /// This method should be called by the extending designer for each message the control would normally receive. This allows the designer to pre-process messages before allowing them to be routed to the control. + /// + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_SETCURSOR: + // We always handle setting the cursor ourselves. + OnSetCursor(); + break; + case NativeMethods.WM_CONTEXTMENU: + // We must handle this ourselves. Control only allows regular Windows Forms context menus, which doesn't do us much good. Also, control's button up processing calls DefwndProc first, which causes a right mouse up to be routed as a WM_CONTEXTMENU. If we don't respond to it here, this message will be bubbled up to our parent, which would pop up a container context menu instead of our own. + int x = NativeMethods.Util.SignedLOWORD(unchecked((int)(long)m.LParam)); + int y = NativeMethods.Util.SignedHIWORD(unchecked((int)(long)m.LParam)); + if (x == -1 && y == -1) + { + // for shift-F10 + Point mouse = Control.MousePosition; + x = mouse.X; + y = mouse.Y; + } + OnContextMenu(x, y); + break; + case NativeMethods.WM_NCHITTEST: + if (_tray.glyphManager != null) + { + // Make sure tha we send our glyphs hit test messages over the TrayControls too + Point pt = new Point((short)NativeMethods.Util.LOWORD(unchecked((int)(long)m.LParam)), (short)NativeMethods.Util.HIWORD(unchecked((int)(long)m.LParam))); + NativeMethods.POINT pt1 = new NativeMethods.POINT + { + x = 0, + y = 0 + }; + NativeMethods.MapWindowPoints(IntPtr.Zero, Handle, pt1, 1); + pt.Offset(pt1.x, pt1.y); + pt.Offset(Location.X, Location.Y);//offset the loc of the traycontrol -so now we're in comptray coords + _tray.glyphManager.GetHitTest(pt); + } + base.WndProc(ref m); + break; + default: + base.WndProc(ref m); + break; + } + } + + private class TrayControlAccessibleObject : ControlAccessibleObject + { + readonly ComponentTray _tray; + public TrayControlAccessibleObject(TrayControl owner, ComponentTray tray) : base(owner) + { + _tray = tray; + } + + private IComponent Component + { + get => ((TrayControl)Owner).Component; + } + + public override AccessibleStates State + { + get + { + AccessibleStates state = base.State; + ISelectionService s = (ISelectionService)_tray.GetService(typeof(ISelectionService)); + if (s != null) + { + if (s.GetComponentSelected(Component)) + { + state |= AccessibleStates.Selected; + } + if (s.PrimarySelection == Component) + { + state |= AccessibleStates.Focused; + } + } + return state; + } + } + } + } + + private class ComponentTrayGlyphManager + { + private Adorner _traySelectionAdorner; //we'll use a single adorner to manage the glyphs + private Glyph _hitTestedGlyph; //the last glyph we hit tested (can be null) + private readonly ISelectionService _selSvc; //we need the selection service fo r the hover behavior + private readonly BehaviorService _behaviorSvc; + + /// + /// Constructor that simply creates an empty adorner. + /// + public ComponentTrayGlyphManager(ISelectionService selSvc, BehaviorService behaviorSvc) + { + _selSvc = selSvc; + _behaviorSvc = behaviorSvc; + _traySelectionAdorner = new Adorner(); + } + + /// + /// This is how we publically expose our glyph collection so that other designer services can 'add value'. + /// + public GlyphCollection SelectionGlyphs + { + get => _traySelectionAdorner.Glyphs; + } + + /// + /// Clears teh adorner of glyphs. + /// + public void Dispose() + { + if (_traySelectionAdorner != null) + { + _traySelectionAdorner.Glyphs.Clear(); + _traySelectionAdorner = null; + } + } + + /// + /// Retrieves a list of glyphs associated with the component. + /// + public GlyphCollection GetGlyphsForComponent(IComponent comp) + { + GlyphCollection glyphs = new GlyphCollection(); + if (_behaviorSvc != null && comp != null) + { + if (_behaviorSvc.DesignerActionUI != null) + { + Glyph g = _behaviorSvc.DesignerActionUI.GetDesignerActionGlyph(comp); + if (g != null) + { + glyphs.Add(g); + } + } + } + return glyphs; + } + + /// + /// Called from the tray's NCHITTEST message in the WndProc. We use this to loop through our glyphs and identify which one is successfully hit tested. From here, we know where to send our messages. + /// + public Cursor GetHitTest(Point p) + { + for (int i = 0; i < _traySelectionAdorner.Glyphs.Count; i++) + { + Cursor hitTestCursor = _traySelectionAdorner.Glyphs[i].GetHitTest(p); + if (hitTestCursor != null) + { + _hitTestedGlyph = _traySelectionAdorner.Glyphs[i]; + return hitTestCursor; + } + } + _hitTestedGlyph = null; + return null; + } + + + /// + /// Called when the tray receives this mouse message. Here, we'll give our glyphs the first chance to repsond to the message before the tray even sees it. + /// + public bool OnMouseDoubleClick(MouseEventArgs e) + { + if (_hitTestedGlyph != null && _hitTestedGlyph.Behavior != null) + { + return _hitTestedGlyph.Behavior.OnMouseDoubleClick(_hitTestedGlyph, e.Button, new Point(e.X, e.Y)); + } + return false; + } + + /// + /// Called when the tray receives this mouse message. Here, we'll give our glyphs the first chance to repsond to the message before the tray even sees it. + /// + public bool OnMouseDown(MouseEventArgs e) + { + if (_hitTestedGlyph != null && _hitTestedGlyph.Behavior != null) + { + return _hitTestedGlyph.Behavior.OnMouseDown(_hitTestedGlyph, e.Button, new Point(e.X, e.Y)); + } + return false; + } + + + /// + /// Called when the tray receives this mouse message. Here, we'll give our glyphs the first chance to repsond to the message before the tray even sees it. + /// + public bool OnMouseMove(MouseEventArgs e) + { + if (_hitTestedGlyph != null && _hitTestedGlyph.Behavior != null) + { + return _hitTestedGlyph.Behavior.OnMouseMove(_hitTestedGlyph, e.Button, new Point(e.X, e.Y)); + } + return false; + } + + /// + /// Called when the tray receives this mouse message. Here, we'll give our glyphs the first chance to repsond to the message before the tray even sees it. + /// + public bool OnMouseUp(MouseEventArgs e) + { + if (_hitTestedGlyph != null && _hitTestedGlyph.Behavior != null) + { + return _hitTestedGlyph.Behavior.OnMouseUp(_hitTestedGlyph, e.Button); + } + return false; + } + + /// + /// Called when the comp tray or any tray control paints. This will simply enumerate through the glyphs in our Adorner and ask them to paint + /// + public void OnPaintGlyphs(PaintEventArgs pe) + { + //Paint any glyphs our tray adorner has + foreach (Glyph g in _traySelectionAdorner.Glyphs) + { + g.Paint(pe); + } + } + + /// + /// Called when a tray control's location has changed. We'll loop through our glyphs and invalidate any that are associated with the component. + /// + public void UpdateLocation(TrayControl trayControl) + { + foreach (Glyph g in _traySelectionAdorner.Glyphs) + { + //only look at glyphs that derive from designerglyph base (actions) + if (g is DesignerActionGlyph desGlyph && ((DesignerActionBehavior)(desGlyph.Behavior)).RelatedComponent.Equals(trayControl.Component)) + { + desGlyph.UpdateAlternativeBounds(trayControl.Bounds); + } + } + } + } + + internal class AutoArrangeComparer : IComparer + { + int IComparer.Compare(object o1, object o2) + { + Debug.Assert(o1 != null && o2 != null, "Null objects sent for comparison!!!"); + Point tcLoc1 = ((Control)o1).Location; + Point tcLoc2 = ((Control)o2).Location; + int height = ((Control)o1).Height / 2; + // If they are at the same location, they are equal. + if (tcLoc1.X == tcLoc2.X && tcLoc1.Y == tcLoc2.Y) + return 0; + // Is the first control lower than the 2nd... + if (tcLoc1.Y + height <= tcLoc2.Y) + return -1; + // Is the 2nd control lower than the first... + if (tcLoc2.Y + height <= tcLoc1.Y) + return 1; + // Which control is left of the other... + return ((tcLoc1.X <= tcLoc2.X) ? -1 : 1); + } + } + + private class TraySelectionUIHandler : SelectionUIHandler + { + private readonly ComponentTray _tray; + private Size _snapSize = Size.Empty; + + /// + /// Creates a new selection UI handler for the given component tray. + /// + public TraySelectionUIHandler(ComponentTray tray) + { + _tray = tray; + _snapSize = new Size(); + } + + /// + /// Called when the user has started the drag. + /// + public override bool BeginDrag(object[] components, SelectionRules rules, int initialX, int initialY) + { + bool value = base.BeginDrag(components, rules, initialX, initialY); + _tray.SuspendLayout(); + return value; + } + + /// + /// Called when the user has completed the drag. The designer should remove any UI feedback it may be providing. + /// + public override void EndDrag(object[] components, bool cancel) + { + base.EndDrag(components, cancel); + _tray.ResumeLayout(); + } + + /// + /// Retrieves the base component for the selection handler. + /// + protected override IComponent GetComponent() + { + return _tray; + } + + /// + /// Retrieves the base component's UI control for the selection handler. + /// + protected override Control GetControl() + { + return _tray; + } + + /// + /// Retrieves the UI control for the given component. + /// + protected override Control GetControl(IComponent component) + { + return TrayControl.FromComponent(component); + } + + /// + /// Retrieves the current grid snap size we should snap objects to. + /// + protected override Size GetCurrentSnapSize() + { + return _snapSize; + } + + /// + /// We use this to request often-used services. + /// + protected override object GetService(Type serviceType) + { + return _tray.GetService(serviceType); + } + + /// + /// Determines if the selection UI handler should attempt to snap objects to a grid. + /// + protected override bool GetShouldSnapToGrid() + { + return false; + } + + /// + /// Given a rectangle, this updates the dimensions of it with any grid snaps and returns a new rectangle. If no changes to the rectangle's size were needed, this may return the same rectangle. + /// + public override Rectangle GetUpdatedRect(Rectangle originalRect, Rectangle dragRect, bool updateSize) + { + return dragRect; + } + + /// + /// Asks the handler to set the appropriate cursor + /// + public override void SetCursor() + { + _tray.OnSetCursor(); + } + } + + private class TrayOleDragDropHandler : OleDragDropHandler + { + public TrayOleDragDropHandler(SelectionUIHandler selectionHandler, IServiceProvider serviceProvider, IOleDragClient client) : base(selectionHandler, serviceProvider, client) + { + } + + protected override bool CanDropDataObject(IDataObject dataObj) + { + ICollection comps = null; + if (dataObj != null) + { + if (dataObj is ComponentDataObjectWrapper cdow) + { + ComponentDataObject cdo = (ComponentDataObject)cdow.InnerData; + comps = cdo.Components; + } + else + { + try + { + object serializationData = dataObj.GetData(OleDragDropHandler.DataFormat, true); + if (serializationData == null) + { + return false; + } + + IDesignerSerializationService ds = (IDesignerSerializationService)GetService(typeof(IDesignerSerializationService)); + if (ds == null) + { + return false; + } + comps = ds.Deserialize(serializationData); + } + catch (Exception e) + { + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + // we return false on any exception + } + } + } + + if (comps != null && comps.Count > 0) + { + foreach (object comp in comps) + { + if (comp is Point) + { + continue; + } + if (comp is Control || !(comp is IComponent)) + { + return false; + } + } + return true; + } + return false; + } } } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventArgs.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventArgs.cs new file mode 100644 index 00000000000..b0aa7f18f70 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventArgs.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Design +{ + /// + /// Provides data for the event. + /// + internal class ContainerSelectorActiveEventArgs : EventArgs + { + private readonly object _component; + private readonly ContainerSelectorActiveEventArgsType _eventType; + + /// + /// Initializes a new instance of the 'ContainerSelectorActiveEventArgs' class. + /// + public ContainerSelectorActiveEventArgs(object component) : this(component, ContainerSelectorActiveEventArgsType.Mouse) + { + } + + /// + /// Initializes a new instance of the 'ContainerSelectorActiveEventArgs' class. + /// + public ContainerSelectorActiveEventArgs(object component, ContainerSelectorActiveEventArgsType eventType) + { + _component = component; + _eventType = eventType; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventArgsType.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventArgsType.cs new file mode 100644 index 00000000000..ad86ebeb6c0 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventArgsType.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Design +{ + /// + /// Specifies IDs for containers of certain event types. + /// + internal enum ContainerSelectorActiveEventArgsType + { + /// + /// Indicates the container of the active event was the contextmenu. + /// + Contextmenu = 1, + /// + /// Indicates the container of the active event was the mouse. + /// + Mouse = 2, + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventHandler.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventHandler.cs new file mode 100644 index 00000000000..d896603bd09 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventHandler.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Forms.Design.ContainerSelectorActiveEventHandler..ctor(System.Object,System.IntPtr)")] +namespace System.Windows.Forms.Design +{ + /// + /// Represents the method that will handle a ContainerSelectorActive event. + /// + internal delegate void ContainerSelectorActiveEventHandler(object sender, ContainerSelectorActiveEventArgs e); +} + diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContextMenuStripActionList.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContextMenuStripActionList.cs new file mode 100644 index 00000000000..49468c2c5ae --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContextMenuStripActionList.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; + +namespace System.Windows.Forms.Design +{ + internal class ContextMenuStripActionList : DesignerActionList + { + private readonly ToolStripDropDown _toolStripDropDown; + private bool _autoShow = false; + + public ContextMenuStripActionList(ToolStripDropDownDesigner designer) : base(designer.Component) + { + _toolStripDropDown = (ToolStripDropDown)designer.Component; + } + + //helper function to get the property on the actual Control + private object GetProperty(string propertyName) + { + PropertyDescriptor getProperty = TypeDescriptor.GetProperties(_toolStripDropDown)[propertyName]; + Debug.Assert(getProperty != null, "Could not find given property in control."); + if (getProperty != null) + { + return getProperty.GetValue(_toolStripDropDown); + } + return null; + } + + //helper function to change the property on the actual Control + private void ChangeProperty(string propertyName, object value) + { + PropertyDescriptor changingProperty = TypeDescriptor.GetProperties(_toolStripDropDown)[propertyName]; + Debug.Assert(changingProperty != null, "Could not find given property in control."); + if (changingProperty != null) + { + changingProperty.SetValue(_toolStripDropDown, value); + } + } + + /// + /// Controls whether the Chrome is Automatically shown on selection + /// + public override bool AutoShow + { + get => _autoShow; + set + { + if (_autoShow != value) + { + _autoShow = value; + } + } + } + + public bool ShowImageMargin + { + get => (bool)GetProperty("ShowImageMargin"); + set + { + if (value != ShowImageMargin) + { + ChangeProperty("ShowImageMargin", (object)value); + } + } + } + + public bool ShowCheckMargin + { + get => (bool)GetProperty("ShowCheckMargin"); + set + { + if (value != ShowCheckMargin) + { + ChangeProperty("ShowCheckMargin", (object)value); + } + } + } + + public ToolStripRenderMode RenderMode + { + get => (ToolStripRenderMode)GetProperty("RenderMode"); + set + { + if (value != RenderMode) + { + ChangeProperty("RenderMode", (object)value); + } + } + } + + /// + /// The Main method to group the ActionItems and pass it to the Panel. + /// + public override DesignerActionItemCollection GetSortedActionItems() + { + DesignerActionItemCollection items = new DesignerActionItemCollection(); + items.Add(new DesignerActionPropertyItem("RenderMode", SR.ToolStripActionList_RenderMode, SR.ToolStripActionList_Layout, SR.ToolStripActionList_RenderModeDesc)); + if (_toolStripDropDown is ToolStripDropDownMenu) + { + items.Add(new DesignerActionPropertyItem("ShowImageMargin", SR.ContextMenuStripActionList_ShowImageMargin, SR.ToolStripActionList_Layout, SR.ContextMenuStripActionList_ShowImageMarginDesc)); + items.Add(new DesignerActionPropertyItem("ShowCheckMargin", SR.ContextMenuStripActionList_ShowCheckMargin, SR.ToolStripActionList_Layout, SR.ContextMenuStripActionList_ShowCheckMarginDesc)); + } + return items; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContextMenuStripGroup.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContextMenuStripGroup.cs new file mode 100644 index 00000000000..ae2f8ce3fc2 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContextMenuStripGroup.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace System.Windows.Forms.Design +{ + internal class ContextMenuStripGroup + { + private List _items; + private readonly string _name; + + public ContextMenuStripGroup(string name) => _name = name; + + public List Items + { + get + { + if (_items == null) + { + _items = new List(); + } + return _items; + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContextMenuStripGroupCollection.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContextMenuStripGroupCollection.cs new file mode 100644 index 00000000000..6c5871432ea --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContextMenuStripGroupCollection.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; + +namespace System.Windows.Forms.Design +{ + internal class ContextMenuStripGroupCollection : DictionaryBase + { + public ContextMenuStripGroupCollection() + { + + } + + public ContextMenuStripGroup this[string key] + { + get + { + if (!InnerHashtable.ContainsKey(key)) + { + InnerHashtable[key] = new ContextMenuStripGroup(key); + } + return InnerHashtable[key] as ContextMenuStripGroup; + } + } + + public bool ContainsKey(string key) + { + return InnerHashtable.ContainsKey(key); + } + protected override void OnInsert(object key, object value) + { + if (!(value is ContextMenuStripGroup)) + { + throw new NotSupportedException(); + } + base.OnInsert(key, value); + } + + protected override void OnSet(object key, object oldValue, object newValue) + { + if (!(newValue is ContextMenuStripGroup)) + { + throw new NotSupportedException(); + } + base.OnSet(key, oldValue, newValue); + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs index f6f77241851..229f68c8368 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs @@ -12,6 +12,7 @@ using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Design; +using System.Globalization; using System.Runtime.InteropServices; using System.Windows.Forms.Design.Behavior; using Accessibility; @@ -19,87 +20,79 @@ namespace System.Windows.Forms.Design { /// - /// - /// Provides a designer that can design components - /// that extend Control. - /// + /// Provides a designer that can design components that extend Control. /// public class ControlDesigner : ComponentDesigner { protected static readonly Point InvalidPoint = new Point(int.MinValue, int.MinValue); - private static int currentProcessId; - private IDesignerHost host; // the host for our designer - private IDesignerTarget designerTarget; // the target window proc for the control. - - private bool liveRegion; // is the mouse is over a live region of the control? - private bool inHitTest; // A popular way to implement GetHitTest is by WM_NCHITTEST...which would cause a cycle. - private bool hasLocation; // Do we have a location property? - private bool locationChecked; // And did we check it - private bool locked; // signifies if this control is locked or not - private bool initializing; - private bool enabledchangerecursionguard; + private static int s_currentProcessId; + private IDesignerHost _host; // the host for our designer + private IDesignerTarget _designerTarget; // the target window proc for the control. + + private bool _liveRegion; // is the mouse is over a live region of the control? + private bool _inHitTest; // A popular way to implement GetHitTest is by WM_NCHITTEST...which would cause a cycle. + private bool _hasLocation; // Do we have a location property? + private bool _locationChecked; // And did we check it + private bool _locked; // signifies if this control is locked or not + private bool _enabledchangerecursionguard; //Behavior work - private BehaviorService behaviorService; //we cache this 'cause we use it so often - private ResizeBehavior resizeBehavior; //the standard behavior for our selection glyphs - demand created - private ContainerSelectorBehavior moveBehavior; //the behavior for non-resize glyphs - demand created + private BehaviorService _behaviorService; //we cache this 'cause we use it so often + private ResizeBehavior _resizeBehavior; //the standard behavior for our selection glyphs - demand created + private ContainerSelectorBehavior _moveBehavior; //the behavior for non-resize glyphs - demand created // Services that we use enough to cache - private ISelectionUIService selectionUISvc; - private IEventHandlerService eventSvc; - private IToolboxService toolboxSvc; - private InheritanceUI inheritanceUI; - private IOverlayService overlayService; + private ISelectionUIService _selectionUISvc; + private IEventHandlerService _eventSvc; + private IToolboxService _toolboxSvc; + private InheritanceUI _inheritanceUI; + private IOverlayService _overlayService; // transient values that are used during mouse drags - private Point mouseDragLast = InvalidPoint; // the last position of the mouse during a drag. - private bool mouseDragMoved; // has the mouse been moved during this drag? - private int lastMoveScreenX; - private int lastMoveScreenY; + private Point _mouseDragLast = InvalidPoint; // the last position of the mouse during a drag. + private bool _mouseDragMoved; // has the mouse been moved during this drag? + private int _lastMoveScreenX; + private int _lastMoveScreenY; // Values used to simulate double clicks for controls that don't support them. - private int lastClickMessageTime; - private int lastClickMessagePositionX; - private int lastClickMessagePositionY; - - private Point downPos = Point.Empty; // point used to track first down of a double click - private event EventHandler disposingHandler; - private CollectionChangeEventHandler dataBindingsCollectionChanged; - private Exception thrownException; - - private bool ctrlSelect; // if the CTRL key was down at the mouse down - private bool toolPassThrough; // a tool is selected, allow the parent to draw a rect for it. - private bool removalNotificationHooked = false; - private bool revokeDragDrop = true; - private bool hadDragDrop; - private DesignerControlCollection controls; - private static bool inContextMenu = false; - private DockingActionList dockingAction; - private StatusCommandUI statusCommandUI; // UI for setting the StatusBar Information.. - - private bool forceVisible = true; - private bool autoResizeHandles = false; // used for disabling AutoSize effect on resize modes. Needed for compat. - private Dictionary subclassedChildren; + private int _lastClickMessageTime; + private int _lastClickMessagePositionX; + private int _lastClickMessagePositionY; + + private Point _downPos = Point.Empty; // point used to track first down of a double click + private event EventHandler DisposingHandler; + private CollectionChangeEventHandler _dataBindingsCollectionChanged; + private Exception _thrownException; + + private bool _ctrlSelect; // if the CTRL key was down at the mouse down + private bool _toolPassThrough; // a tool is selected, allow the parent to draw a rect for it. + private bool _removalNotificationHooked = false; + private bool _revokeDragDrop = true; + private bool _hadDragDrop; + private static bool s_inContextMenu = false; + private DockingActionList _dockingAction; + private StatusCommandUI _statusCommandUI; // UI for setting the StatusBar Information.. + + private bool _forceVisible = true; + private bool _autoResizeHandles = false; // used for disabling AutoSize effect on resize modes. Needed for compat. + private Dictionary _subclassedChildren; protected BehaviorService BehaviorService { get { - if (behaviorService == null) + if (_behaviorService == null) { - behaviorService = (BehaviorService)GetService(typeof(BehaviorService)); + _behaviorService = (BehaviorService)GetService(typeof(BehaviorService)); } - return behaviorService; + return _behaviorService; } } internal bool ForceVisible { - get => forceVisible; - set => forceVisible = value; + get => _forceVisible; + set => _forceVisible = value; } /// - /// - /// Retrieves a list of associated components. These are components that should be incluced in a cut or copy - /// operation on this component. - /// + /// Retrieves a list of associated components. These are components that should be incluced in a cut or copy operation on this component. /// public override ICollection AssociatedComponents { @@ -126,6 +119,8 @@ public override ICollection AssociatedComponents } } + protected AccessibleObject accessibilityObj = null; + public virtual AccessibleObject AccessibilityObject { get @@ -155,11 +150,7 @@ protected virtual bool EnableDragRect } /// - /// Returns the parent component for this control designer. - /// The default implementation just checks to see if the - /// component being designed is a control, and if it is it - /// returns its parent. This property can return null if there - /// is no parent component. + /// Returns the parent component for this control designer. The default implementation just checks to see if the component being designed is a control, and if it is it returns its parent. This property can return null if there is no parent component. /// protected override IComponent ParentComponent { @@ -174,35 +165,46 @@ protected override IComponent ParentComponent } /// - /// Determines whether or not the ControlDesigner will allow SnapLine alignment during a - /// drag operation when the primary drag control is over this designer, or when a control - /// is being dragged from the toolbox, or when a control is being drawn through click-drag. + /// Determines whether or not the ControlDesigner will allow SnapLine alignment during a drag operation when the primary drag control is over this designer, or when a control is being dragged from the toolbox, or when a control is being drawn through click-drag. /// public virtual bool ParticipatesWithSnapLines { get => true; } - /// - /// public bool AutoResizeHandles { - get => autoResizeHandles; - set => autoResizeHandles = value; + get => _autoResizeHandles; + set => _autoResizeHandles = value; + } + + private IDesignerTarget DesignerTarget + { + get => _designerTarget; + set => _designerTarget = value; + } + + private Dictionary SubclassedChildWindows + { + get + { + if (_subclassedChildren == null) + { + _subclassedChildren = new Dictionary(); + } + return _subclassedChildren; + } } /// - /// Retrieves a set of rules concerning the movement capabilities of a component. - /// This should be one or more flags from the SelectionRules class. If no designer - /// provides rules for a component, the component will not get any UI services. + /// Retrieves a set of rules concerning the movement capabilities of a component. This should be one or more flags from the SelectionRules class. If no designer provides rules for a component, the component will not get any UI services. /// public virtual SelectionRules SelectionRules { get { - SelectionRules rules = SelectionRules.Visible; object component = Component; - rules = SelectionRules.Visible; + SelectionRules rules = SelectionRules.Visible; PropertyDescriptor prop; PropertyDescriptorCollection props = TypeDescriptor.GetProperties(component); PropertyDescriptor autoSizeProp = props["AutoSize"]; @@ -215,7 +217,7 @@ public virtual SelectionRules SelectionRules if ((prop = props["Size"]) != null && !prop.IsReadOnly) { - if (AutoResizeHandles && this.Component != host.RootComponent) + if (AutoResizeHandles && Component != _host.RootComponent) { rules = IsResizableConsiderAutoSize(autoSizeProp, autoSizeModeProp) ? rules | SelectionRules.AllSizeable : rules; } @@ -264,7 +266,7 @@ public virtual SelectionRules SelectionRules PropertyDescriptor pd = props["Locked"]; if (pd != null) { - Object value = pd.GetValue(component); + object value = pd.GetValue(component); // make sure that value is a boolean, in case someone else added this property if (value is bool && (bool)value == true) { @@ -275,6 +277,24 @@ public virtual SelectionRules SelectionRules } } + internal virtual bool ControlSupportsSnaplines + { + get => true; + } + + internal Point GetOffsetToClientArea() + { + NativeMethods.POINT nativeOffset = new NativeMethods.POINT(0, 0); + NativeMethods.MapWindowPoints(Control.Handle, Control.Parent.Handle, nativeOffset, 1); + Point offset = Control.Location; + // If the 2 controls do not have the same orientation, then force one to make sure we calculate the correct offset + if (Control.IsMirrored != Control.Parent.IsMirrored) + { + offset.Offset(Control.Width, 0); + } + return (new Point(Math.Abs(nativeOffset.x - offset.X), nativeOffset.y - offset.Y)); + } + private bool IsResizableConsiderAutoSize(PropertyDescriptor autoSizeProp, PropertyDescriptor autoSizeModeProp) { object component = Component; @@ -302,10 +322,7 @@ private bool IsResizableConsiderAutoSize(PropertyDescriptor autoSizeProp, Proper } /// - /// Returns a list of SnapLine objects representing interesting - /// alignment points for this control. These SnapLines are used - /// to assist in the positioning of the control on a parent's - /// surface. + /// Returns a list of SnapLine objects representing interesting alignment points for this control. These SnapLines are used to assist in the positioning of the control on a parent's surface. /// public virtual IList SnapLines { @@ -349,16 +366,14 @@ protected override InheritanceAttribute InheritanceAttribute } } - internal bool IsRootDesigner + internal new bool IsRootDesigner { get { - Debug.Assert(this.Component != null, - "this.component needs to be set before this method is valid."); - + Debug.Assert(Component != null, "this.component needs to be set before this method is valid."); bool isRoot = false; IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); - if (host != null && this.Component == host.RootComponent) + if (host != null && Component == host.RootComponent) { isRoot = true; } @@ -367,56 +382,31 @@ internal bool IsRootDesigner } /// - /// Returns the number of internal control designers in the ControlDesigner. An internal control - /// is a control that is not in the IDesignerHost.Container.Components collection. - /// SplitterPanel is an example of one such control. We use this to get SnapLines for the internal - /// control designers. + /// Returns the number of internal control designers in the ControlDesigner. An internal control is a control that is not in the IDesignerHost.Container.Components collection. SplitterPanel is an example of one such control. We use this to get SnapLines for the internal control designers. /// - public virtual int NumberOfInternalControlDesigners() - { - return 0; - } + public virtual int NumberOfInternalControlDesigners() => 0; /// - /// Returns the internal control designer with the specified index in the ControlDesigner. An internal control - /// is a control that is not in the IDesignerHost.Container.Components collection. - /// SplitterPanel is an example of one such control. - /// internalControlIndex is zero-based. + /// Returns the internal control designer with the specified index in the ControlDesigner. An internal control is a control that is not in the IDesignerHost.Container.Components collection. SplitterPanel is an example of one such control. internalControlIndex is zero-based. /// - public virtual ControlDesigner InternalControlDesigner(int internalControlIndex) - { - return null; - } + public virtual ControlDesigner InternalControlDesigner(int internalControlIndex) => null; /// - /// Default processing for messages. This method causes the message to - /// get processed by windows, skipping the control. This is useful if - /// you want to block this message from getting to the control, but - /// you do not want to block it from getting to Windows itself because - /// it causes other messages to be generated. + /// Default processing for messages. This method causes the message to get processed by windows, skipping the control. This is useful if you want to block this message from getting to the control, but you do not want to block it from getting to Windows itself because it causes other messages to be generated. /// - protected void BaseWndProc(ref Message m) - { - m.Result = NativeMethods.DefWindowProc(m.HWnd, m.Msg, m.WParam, m.LParam); - } + protected void BaseWndProc(ref Message m) => m.Result = UnsafeNativeMethods.DefWindowProc(m.HWnd, m.Msg, m.WParam, m.LParam); /// - /// Determines if the this designer can be parented to the specified desinger -- - /// generally this means if the control for this designer can be parented into the - /// given ParentControlDesigner's designer. + /// Determines if the this designer can be parented to the specified desinger -- generally this means if the control for this designer can be parented into the given ParentControlDesigner's designer. /// - public virtual bool CanBeParentedTo(IDesigner parentDesigner) - { - return parentDesigner is ParentControlDesigner p && !Control.Contains(p.Control); - } + public virtual bool CanBeParentedTo(IDesigner parentDesigner) => parentDesigner is ParentControlDesigner p && !Control.Contains(p.Control); /// - /// Default processing for messages. This method causes the message to - /// get processed by the control, rather than the designer. + /// Default processing for messages. This method causes the message to get processed by the control, rather than the designer. /// protected void DefWndProc(ref Message m) { - designerTarget.DefWndProc(ref m); + _designerTarget.DefWndProc(ref m); } /// @@ -449,39 +439,39 @@ protected override void Dispose(bool disposing) { if (Control != null) { - if (dataBindingsCollectionChanged != null) + if (_dataBindingsCollectionChanged != null) { - Control.DataBindings.CollectionChanged -= dataBindingsCollectionChanged; + Control.DataBindings.CollectionChanged -= _dataBindingsCollectionChanged; } - if (Inherited && inheritanceUI != null) + if (Inherited && _inheritanceUI != null) { - inheritanceUI.RemoveInheritedControl(Control); + _inheritanceUI.RemoveInheritedControl(Control); } - if (removalNotificationHooked) + if (_removalNotificationHooked) { IComponentChangeService csc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); if (csc != null) { csc.ComponentRemoved -= new ComponentEventHandler(DataSource_ComponentRemoved); } - removalNotificationHooked = false; + _removalNotificationHooked = false; } - disposingHandler?.Invoke(this, EventArgs.Empty); + DisposingHandler?.Invoke(this, EventArgs.Empty); UnhookChildControls(Control); } if (ContextMenu != null) { - ContextMenu.Disposed -= new EventHandler(this.DetachContextMenu); + ContextMenu.Disposed -= new EventHandler(DetachContextMenu); } - if (designerTarget != null) + if (_designerTarget != null) { - designerTarget.Dispose(); + _designerTarget.Dispose(); } - downPos = Point.Empty; + _downPos = Point.Empty; Control.ControlAdded -= new ControlEventHandler(OnControlAdded); Control.ControlRemoved -= new ControlEventHandler(OnControlRemoved); Control.ParentChanged -= new EventHandler(OnParentChanged); @@ -494,9 +484,9 @@ protected override void Dispose(bool disposing) private void OnControlAdded(object sender, ControlEventArgs e) { - if (e.Control != null && host != null) + if (e.Control != null && _host != null) { - if (!(host.GetDesigner(e.Control) is ControlDesigner)) + if (!(_host.GetDesigner(e.Control) is ControlDesigner)) { // No, no designer means we must replace the window target in this control. IWindowTarget oldTarget = e.Control.WindowTarget; @@ -511,7 +501,7 @@ private void OnControlAdded(object sender, ControlEventArgs e) if (e.Control.IsHandleCreated) { Application.OleRequired(); - NativeMethods.RevokeDragDrop(e.Control.Handle); + UnsafeNativeMethods.RevokeDragDrop(e.Control.Handle); // We only hook the control's children if there was no designer. We leave it up to the designer to hook its own children. HookChildControls(e.Control); } @@ -524,90 +514,6 @@ private interface IDesignerTarget : IDisposable void DefWndProc(ref Message m); } - private class ChildWindowTarget : IWindowTarget, IDesignerTarget - { - private ControlDesigner designer; - private Control childControl; - private IWindowTarget oldWindowTarget; - private IntPtr handle = IntPtr.Zero; - - public ChildWindowTarget(ControlDesigner designer, Control childControl, IWindowTarget oldWindowTarget) - { - this.designer = designer; - this.childControl = childControl; - this.oldWindowTarget = oldWindowTarget; - } - - public IWindowTarget OldWindowTarget - { - get - { - return oldWindowTarget; - } - } - - public void DefWndProc(ref Message m) - { - oldWindowTarget.OnMessage(ref m); - } - - [SuppressMessage("Microsoft.Usage", "CA2216:DisposableTypesShouldDeclareFinalizer")] - public void Dispose() - { - // Do nothing. We will pick this up through a null DesignerTarget property when we come out of the message loop. - } - - public void OnHandleChange(IntPtr newHandle) - { - handle = newHandle; - oldWindowTarget.OnHandleChange(newHandle); - } - - public void OnMessage(ref Message m) - { - // If the designer has jumped ship, the continue partying on messages, but send them back to the original control. - if (designer.Component == null) - { - oldWindowTarget.OnMessage(ref m); - return; - } - - // We want these messages to go through the designer's WndProc method, and we want people to be able to do default processing with the designer's DefWndProc. So, we stuff the old window target into the designer's target and then call their WndProc. - IDesignerTarget designerTarget = designer.DesignerTarget; - designer.DesignerTarget = this; - - try - { - designer.WndProc(ref m); - } - catch (Exception ex) - { - designer.SetUnhandledException(childControl, ex); - } - finally - { - - // If the designer disposed us, then we should follow suit. - // - if (designer.DesignerTarget == null) - { - designerTarget.Dispose(); - } - else - { - designer.DesignerTarget = designerTarget; - } - - // Controls (primarily RichEdit) will register themselves as drag-drop source/targets when they are instantiated. Normally, when they are being designed, we will RevokeDragDrop() in their designers. The problem occurs when these controls are inside a UserControl. At that time, we do not have a designer for these controls, and they prevent the ParentControlDesigner's drag-drop from working. What we do is to loop through all child controls that do not have a designer (in HookChildControls()), and RevokeDragDrop() after their handles have been created. - if (m.Msg == NativeMethods.WM_CREATE) - { - Debug.Assert(handle != IntPtr.Zero, "Handle for control not created"); - NativeMethods.RevokeDragDrop(handle); - } - } - } - } - private void DetachContextMenu(object sender, EventArgs e) { ContextMenu = null; @@ -646,7 +552,7 @@ private void DataSource_ComponentRemoved(object sender, ComponentEventArgs e) if (Component is Control ctl) { Debug.Assert(ctl.DataBindings.Count > 0, "we should not be notified if the control has no dataBindings"); - ctl.DataBindings.CollectionChanged -= dataBindingsCollectionChanged; + ctl.DataBindings.CollectionChanged -= _dataBindingsCollectionChanged; for (int i = 0; i < ctl.DataBindings.Count; i++) { Binding binding = ctl.DataBindings[i]; @@ -665,27 +571,14 @@ private void DataSource_ComponentRemoved(object sender, ComponentEventArgs e) { csc.ComponentRemoved -= new ComponentEventHandler(DataSource_ComponentRemoved); } - removalNotificationHooked = false; + _removalNotificationHooked = false; } - ctl.DataBindings.CollectionChanged += dataBindingsCollectionChanged; + ctl.DataBindings.CollectionChanged += _dataBindingsCollectionChanged; } } /// - /// Enables design time functionality for a child control. The child control is a child - /// of this control designer's control. The child does not directly participate in - /// persistence, but it will if it is exposed as a property of the main control. Consider - /// a control like the SplitContainer: it has two panels, Panel1 and Panel2. These panels - /// are exposed through read only Panel1 and Panel2 properties on the SplitContainer class. - /// SplitContainer's designer calls EnableDesignTime for each panel, which allows other - /// components to be dropped on them. But, in order for the contents of Panel1 and Panel2 - /// to be saved, SplitContainer itself needed to expose the panels as public properties. - /// The child paramter is the control to enable. The name paramter is the name of this - /// control as exposed to the end user. Names need to be unique within a control designer, - /// but do not have to be unique to other control designer's children. - /// This method returns true if the child control could be enabled for design time, or - /// false if the hosting infrastructure does not support it. To support this feature, the - /// hosting infrastructure must expose the INestedContainer class as a service off of the site. + /// Enables design time functionality for a child control. The child control is a child of this control designer's control. The child does not directly participate in persistence, but it will if it is exposed as a property of the main control. Consider a control like the SplitContainer: it has two panels, Panel1 and Panel2. These panels are exposed through read only Panel1 and Panel2 properties on the SplitContainer class. SplitContainer's designer calls EnableDesignTime for each panel, which allows other components to be dropped on them. But, in order for the contents of Panel1 and Panel2 to be saved, SplitContainer itself needed to expose the panels as public properties. The child paramter is the control to enable. The name paramter is the name of this control as exposed to the end user. Names need to be unique within a control designer, but do not have to be unique to other control designer's children. This method returns true if the child control could be enabled for design time, or false if the hosting infrastructure does not support it. To support this feature, the hosting infrastructure must expose the INestedContainer class as a service off of the site. /// protected bool EnableDesignMode(Control child, string name) { @@ -717,8 +610,7 @@ protected bool EnableDesignMode(Control child, string name) } /// - /// Enables or disables drag/drop support. This - /// hooks drag event handlers to the control. + /// Enables or disables drag/drop support. This hooks drag event handlers to the control. /// protected void EnableDragDrop(bool value) { @@ -730,37 +622,82 @@ protected void EnableDragDrop(bool value) if (value) { - rc.DragDrop += new DragEventHandler(this.OnDragDrop); - rc.DragOver += new DragEventHandler(this.OnDragOver); - rc.DragEnter += new DragEventHandler(this.OnDragEnter); - rc.DragLeave += new EventHandler(this.OnDragLeave); - rc.GiveFeedback += new GiveFeedbackEventHandler(this.OnGiveFeedback); - hadDragDrop = rc.AllowDrop; - if (!hadDragDrop) + rc.DragDrop += new DragEventHandler(OnDragDrop); + rc.DragOver += new DragEventHandler(OnDragOver); + rc.DragEnter += new DragEventHandler(OnDragEnter); + rc.DragLeave += new EventHandler(OnDragLeave); + rc.GiveFeedback += new GiveFeedbackEventHandler(OnGiveFeedback); + _hadDragDrop = rc.AllowDrop; + if (!_hadDragDrop) { rc.AllowDrop = true; } - revokeDragDrop = false; + _revokeDragDrop = false; } else { - rc.DragDrop -= new DragEventHandler(this.OnDragDrop); - rc.DragOver -= new DragEventHandler(this.OnDragOver); - rc.DragEnter -= new DragEventHandler(this.OnDragEnter); - rc.DragLeave -= new EventHandler(this.OnDragLeave); - rc.GiveFeedback -= new GiveFeedbackEventHandler(this.OnGiveFeedback); - if (!hadDragDrop) + rc.DragDrop -= new DragEventHandler(OnDragDrop); + rc.DragOver -= new DragEventHandler(OnDragOver); + rc.DragEnter -= new DragEventHandler(OnDragEnter); + rc.DragLeave -= new EventHandler(OnDragLeave); + rc.GiveFeedback -= new GiveFeedbackEventHandler(OnGiveFeedback); + if (!_hadDragDrop) { rc.AllowDrop = false; } - revokeDragDrop = true; + _revokeDragDrop = true; + } + } + + private void OnGiveFeedback(object s, GiveFeedbackEventArgs e) + { + OnGiveFeedback(e); + } + + private void OnDragLeave(object s, EventArgs e) + { + OnDragLeave(e); + } + + private void OnDragEnter(object s, DragEventArgs e) + { + if (BehaviorService != null) + { + //Tell the BehaviorService to monitor mouse messages so it can send appropriate drag notifications. + BehaviorService.StartDragNotification(); + } + OnDragEnter(e); + } + + private void OnDragOver(object s, DragEventArgs e) + { + OnDragOver(e); + } + + private void OnDragDrop(object s, DragEventArgs e) + { + if (BehaviorService != null) + { + //this will cause the BehSvc to return from 'drag mode' + BehaviorService.EndDragNotification(); + } + OnDragDrop(e); + } + + internal System.Windows.Forms.Design.Behavior.Behavior MoveBehavior + { + get + { + if (_moveBehavior == null) + { + _moveBehavior = new ContainerSelectorBehavior(Control, Component.Site); + } + return _moveBehavior; } } /// - /// Returns a 'BodyGlyph' representing the bounds of this control. - /// The BodyGlyph is responsible for hit testing the related CtrlDes - /// and forwarding messages directly to the designer. + /// Returns a 'BodyGlyph' representing the bounds of this control. The BodyGlyph is responsible for hit testing the related CtrlDes and forwarding messages directly to the designer. /// protected virtual ControlBodyGlyph GetControlGlyph(GlyphSelectionType selectionType) { @@ -772,7 +709,7 @@ protected virtual ControlBodyGlyph GetControlGlyph(GlyphSelectionType selectionT //create our glyph, and set its cursor appropriately ControlBodyGlyph g = null; Control parent = Control.Parent; - if (parent != null && host != null && host.RootComponent != Component) + if (parent != null && _host != null && _host.RootComponent != Component) { Rectangle parentRect = parent.RectangleToScreen(parent.ClientRectangle); Rectangle controlRect = Control.RectangleToScreen(Control.ClientRectangle); @@ -801,12 +738,10 @@ protected virtual ControlBodyGlyph GetControlGlyph(GlyphSelectionType selectionT return g; } + internal ControlBodyGlyph GetControlGlyphInternal(GlyphSelectionType selectionType) => GetControlGlyph(selectionType); + /// - /// Returns a collection of Glyph objects representing the selection - /// borders and grab handles for a standard control. Note that - /// based on 'selectionType' the Glyphs returned will either: represent - /// a fully resizeable selection border with grab handles, a locked - /// selection border, or a single 'hidden' selection Glyph. + /// Returns a collection of Glyph objects representing the selection borders and grab handles for a standard control. Note that based on 'selectionType' the Glyphs returned will either: represent a fully resizeable selection border with grab handles, a locked selection border, or a single 'hidden' selection Glyph. /// public virtual GlyphCollection GetGlyphs(GlyphSelectionType selectionType) { @@ -836,9 +771,9 @@ public virtual GlyphCollection GetGlyphs(GlyphSelectionType selectionType) glyphs.Add(new NoResizeSelectionBorderGlyph(translatedBounds, rules, SelectionBorderGlyphType.Left, MoveBehavior)); glyphs.Add(new NoResizeSelectionBorderGlyph(translatedBounds, rules, SelectionBorderGlyphType.Right, MoveBehavior)); // enable the designeractionpanel for this control if it needs one - if (TypeDescriptor.GetAttributes(Component).Contains(DesignTimeVisibleAttribute.Yes) && behaviorService.DesignerActionUI != null) + if (TypeDescriptor.GetAttributes(Component).Contains(DesignTimeVisibleAttribute.Yes) && _behaviorService.DesignerActionUI != null) { - Glyph dapGlyph = behaviorService.DesignerActionUI.GetDesignerActionGlyph(Component); + Glyph dapGlyph = _behaviorService.DesignerActionUI.GetDesignerActionGlyph(Component); if (dapGlyph != null) { glyphs.Insert(0, dapGlyph); //we WANT to be in front of the other UI @@ -890,9 +825,9 @@ public virtual GlyphCollection GetGlyphs(GlyphSelectionType selectionType) glyphs.Add(new SelectionBorderGlyph(translatedBounds, rules, SelectionBorderGlyphType.Left, StandardBehavior)); glyphs.Add(new SelectionBorderGlyph(translatedBounds, rules, SelectionBorderGlyphType.Right, StandardBehavior)); // enable the designeractionpanel for this control if it needs one - if (TypeDescriptor.GetAttributes(Component).Contains(DesignTimeVisibleAttribute.Yes) && behaviorService.DesignerActionUI != null) + if (TypeDescriptor.GetAttributes(Component).Contains(DesignTimeVisibleAttribute.Yes) && _behaviorService.DesignerActionUI != null) { - Glyph dapGlyph = behaviorService.DesignerActionUI.GetDesignerActionGlyph(Component); + Glyph dapGlyph = _behaviorService.DesignerActionUI.GetDesignerActionGlyph(Component); if (dapGlyph != null) { glyphs.Insert(0, dapGlyph); //we WANT to be in front of the other UI @@ -903,11 +838,25 @@ public virtual GlyphCollection GetGlyphs(GlyphSelectionType selectionType) return glyphs; } + internal virtual Behavior.Behavior StandardBehavior + { + get + { + if (_resizeBehavior == null) + { + _resizeBehavior = new ResizeBehavior(Component.Site); + } + return _resizeBehavior; + } + } + + internal virtual bool SerializePerformLayout + { + get => false; + } + /// - /// Allows your component to support a design time user interface. A TabStrip - /// control, for example, has a design time user interface that allows the user - /// to click the tabs to change tabs. To implement this, TabStrip returns - /// true whenever the given point is within its tabs. + /// Allows your component to support a design time user interface. A TabStrip control, for example, has a design time user interface that allows the user to click the tabs to change tabs. To implement this, TabStrip returns true whenever the given point is within its tabs. /// protected virtual bool GetHitTest(Point point) { @@ -915,17 +864,15 @@ protected virtual bool GetHitTest(Point point) } /// - /// Hooks the children of the given control. We need to do this for - /// child controls that are not in design mode, which is the case - /// for composite controls. + /// Hooks the children of the given control. We need to do this for child controls that are not in design mode, which is the case for composite controls. /// protected void HookChildControls(Control firstChild) { foreach (Control child in firstChild.Controls) { - if (child != null && host != null) + if (child != null && _host != null) { - if (!(host.GetDesigner(child) is ControlDesigner)) + if (!(_host.GetDesigner(child) is ControlDesigner)) { // No, no designer means we must replace the window target in this control. IWindowTarget oldTarget = child.WindowTarget; @@ -937,7 +884,7 @@ protected void HookChildControls(Control firstChild) if (child.IsHandleCreated) { Application.OleRequired(); - NativeMethods.RevokeDragDrop(child.Handle); + UnsafeNativeMethods.RevokeDragDrop(child.Handle); HookChildHandles(child.Handle); } else @@ -951,12 +898,25 @@ protected void HookChildControls(Control firstChild) } } + private void OnChildHandleCreated(object sender, EventArgs e) + { + Control child = sender as Control; + + Debug.Assert(child != null); + + if (child != null) + { + Debug.Assert(child.IsHandleCreated); + HookChildHandles(child.Handle); + } + } + /// /// Called by the host when we're first initialized. /// public override void Initialize(IComponent component) { - // Visibility works as follows If the control's property is not actually set, then set our shadow to true. Otherwise, grab the shadow value from the control directly and then set the control to be visible if it is not the root component. Root components will be set to visible = true in their own time by the view. + // Visibility works as follows: If the control's property is not actually set, then set our shadow to true. Otherwise, grab the shadow value from the control directly and then set the control to be visible if it is not the root component. Root components will be set to visible = true in their own time by the view. PropertyDescriptorCollection props = TypeDescriptor.GetProperties(component.GetType()); PropertyDescriptor visibleProp = props["Visible"]; if (visibleProp == null || visibleProp.PropertyType != typeof(bool) || !visibleProp.ShouldSerializeValue(component)) @@ -977,12 +937,9 @@ public override void Initialize(IComponent component) { Enabled = (bool)enabledProp.GetValue(component); } - - initializing = true; base.Initialize(component); - initializing = false; // And get other commonly used services. - host = (IDesignerHost)GetService(typeof(IDesignerHost)); + _host = (IDesignerHost)GetService(typeof(IDesignerHost)); // this is to create the action in the DAP for this component if it requires docking/undocking logic AttributeCollection attributes = TypeDescriptor.GetAttributes(Component); @@ -990,23 +947,27 @@ public override void Initialize(IComponent component) if (dockingAttribute != null && dockingAttribute.DockingBehavior != DockingBehavior.Never) { // create the action for this control - dockingAction = new DockingActionList(this); + _dockingAction = new DockingActionList(this); //add our 'dock in parent' or 'undock in parent' action if (GetService(typeof(DesignerActionService)) is DesignerActionService das) { - das.Add(Component, dockingAction); + das.Add(Component, _dockingAction); } } // Hook up the property change notifications we need to track. One for data binding. More for control add / remove notifications - dataBindingsCollectionChanged = new CollectionChangeEventHandler(DataBindingsCollectionChanged); - Control.DataBindings.CollectionChanged += dataBindingsCollectionChanged; + _dataBindingsCollectionChanged = new CollectionChangeEventHandler(DataBindingsCollectionChanged); + Control.DataBindings.CollectionChanged += _dataBindingsCollectionChanged; + Control.ControlAdded += new ControlEventHandler(OnControlAdded); Control.ControlRemoved += new ControlEventHandler(OnControlRemoved); Control.ParentChanged += new EventHandler(OnParentChanged); + Control.SizeChanged += new EventHandler(OnSizeChanged); Control.LocationChanged += new EventHandler(OnLocationChanged); - // Replace the control's window target with our own. This allows us to hook messages. - this.DesignerTarget = new DesignerWindowTarget(this); + + // Replace the control's window target with our own. This allows us to hook messages. + DesignerTarget = new DesignerWindowTarget(this); + // If the handle has already been created for this control, invoke OnCreateHandle so we can hookup our child control subclass. if (Control.IsHandleCreated) { @@ -1014,28 +975,135 @@ public override void Initialize(IComponent component) } // If we are an inherited control, notify our inheritance UI - if (Inherited && host != null && host.RootComponent != component) + if (Inherited && _host != null && _host.RootComponent != component) { - inheritanceUI = (InheritanceUI)GetService(typeof(InheritanceUI)); - if (inheritanceUI != null) + _inheritanceUI = (InheritanceUI)GetService(typeof(InheritanceUI)); + if (_inheritanceUI != null) { - inheritanceUI.AddInheritedControl(Control, InheritanceAttribute.InheritanceLevel); + _inheritanceUI.AddInheritedControl(Control, InheritanceAttribute.InheritanceLevel); } } // When we drag one control from one form to another, we will end up here. In this case we do not want to set the control to visible, so check ForceVisible. - if ((host == null || host.RootComponent != component) && ForceVisible) + if ((_host == null || _host.RootComponent != component) && ForceVisible) { Control.Visible = true; } + // Always make controls enabled, event inherited ones. Otherwise we won't be able to select them. Control.Enabled = true; - //we move enabledchanged below the set to avoid any possible stack overflows. this can occur if the parent is not enabled when we set enabled to true. + // we move enabledchanged below the set to avoid any possible stack overflows. this can occur if the parent is not enabled when we set enabled to true. Control.EnabledChanged += new EventHandler(OnEnabledChanged); // And force some shadow properties that we change in the course of initializing the form. AllowDrop = Control.AllowDrop; // update the Status Command - statusCommandUI = new StatusCommandUI(component.Site); + _statusCommandUI = new StatusCommandUI(component.Site); + } + + // This is a workaround to some problems with the ComponentCache that we should fix. When this is removed remember to change ComponentCache's RemoveEntry method back to private (from internal). + private void OnSizeChanged(object sender, EventArgs e) + { + System.ComponentModel.Design.Serialization.ComponentCache cache = + (System.ComponentModel.Design.Serialization.ComponentCache)GetService(typeof(System.ComponentModel.Design.Serialization.ComponentCache)); + object component = Component; + if (cache != null && component != null) + { + cache.RemoveEntry(component); + } + } + + private void OnLocationChanged(object sender, EventArgs e) + { + System.ComponentModel.Design.Serialization.ComponentCache cache = + (System.ComponentModel.Design.Serialization.ComponentCache)GetService(typeof(System.ComponentModel.Design.Serialization.ComponentCache)); + object component = Component; + if (cache != null && component != null) + { + cache.RemoveEntry(component); + } + } + + private void OnParentChanged(object sender, EventArgs e) + { + if (Control.IsHandleCreated) + { + OnHandleChange(); + } + } + + private void OnControlRemoved(object sender, ControlEventArgs e) + { + if (e.Control != null) + { + // No, no designer means we must replace the window target in this control. + if (e.Control.WindowTarget is ChildWindowTarget oldTarget) + { + e.Control.WindowTarget = oldTarget.OldWindowTarget; + } + UnhookChildControls(e.Control); + } + } + + private void DataBindingsCollectionChanged(object sender, CollectionChangeEventArgs e) + { + // It is possible to use the control designer with NON CONTROl types. + if (Component is Control ctl) + { + if (ctl.DataBindings.Count == 0 && _removalNotificationHooked) + { + // remove the notification for the ComponentRemoved event + IComponentChangeService csc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (csc != null) + { + csc.ComponentRemoved -= new ComponentEventHandler(DataSource_ComponentRemoved); + } + _removalNotificationHooked = false; + } + else if (ctl.DataBindings.Count > 0 && !_removalNotificationHooked) + { + // add he notification for the ComponentRemoved event + IComponentChangeService csc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (csc != null) + { + csc.ComponentRemoved += new ComponentEventHandler(DataSource_ComponentRemoved); + } + _removalNotificationHooked = true; + } + } + } + + private void OnEnabledChanged(object sender, EventArgs e) + { + if (!_enabledchangerecursionguard) + { + _enabledchangerecursionguard = true; + try + { + Control.Enabled = true; + } + finally + { + _enabledchangerecursionguard = false; + } + } + } + + private bool AllowDrop + { + get => (bool)ShadowProperties["AllowDrop"]; + set => ShadowProperties["AllowDrop"] = value; + } + + private bool Enabled + { + get => (bool)ShadowProperties["Enabled"]; + set => ShadowProperties["Enabled"] = value; + } + + private bool Visible + { + get => (bool)ShadowProperties["Visible"]; + set => ShadowProperties["Visible"] = value; } /// @@ -1050,8 +1118,7 @@ public override void InitializeExistingComponent(IDictionary defaultValues) if (c != null) { ISite site = c.Site; - ChildWindowTarget target = c.WindowTarget as ChildWindowTarget; - if (site != null && target != null) + if (site != null && c.WindowTarget is ChildWindowTarget target) { c.WindowTarget = target.OldWindowTarget; } @@ -1060,11 +1127,7 @@ public override void InitializeExistingComponent(IDictionary defaultValues) } /// - /// ControlDesigner overrides this method. It will look at the default property for the control and, - /// if it is of type string, it will set this property's value to the name of the component. It only - /// does this if the designer has been configured with this option in the options service. This method - /// also connects the control to its parent and positions it. If you override this method, you should - /// always call base. + /// ControlDesigner overrides this method. It will look at the default property for the control and, if it is of type string, it will set this property's value to the name of the component. It only does this if the designer has been configured with this option in the options service. This method also connects the control to its parent and positions it. If you override this method, you should always call base. /// public override void InitializeNewComponent(IDictionary defaultValues) { @@ -1123,9 +1186,7 @@ public override void InitializeNewComponent(IDictionary defaultValues) } /// - /// Called when the designer is intialized. This allows the designer to provide some - /// meaningful default values in the component. The default implementation of this - /// sets the components's default property to it's name, if that property is a string. + /// Called when the designer is intialized. This allows the designer to provide some meaningful default values in the component. The default implementation of this sets the components's default property to it's name, if that property is a string. /// [Obsolete("This method has been deprecated. Use InitializeNewComponent instead. http://go.microsoft.com/fwlink/?linkid=14202")] public override void OnSetComponentDefaults() @@ -1155,9 +1216,9 @@ protected virtual void OnContextMenu(int x, int y) protected virtual void OnCreateHandle() { OnHandleChange(); - if (revokeDragDrop) + if (_revokeDragDrop) { - NativeMethods.RevokeDragDrop(Control.Handle); + UnsafeNativeMethods.RevokeDragDrop(Control.Handle); } } @@ -1168,7 +1229,7 @@ protected virtual void OnDragEnter(DragEventArgs de) { // unhook our events - we don't want to create an infinite loop. Control control = Control; - DragEventHandler handler = new DragEventHandler(this.OnDragEnter); + DragEventHandler handler = new DragEventHandler(OnDragEnter); control.DragEnter -= handler; ((IDropTarget)Control).OnDragEnter(de); control.DragEnter += handler; @@ -1189,7 +1250,7 @@ protected virtual void OnDragDrop(DragEventArgs de) { // unhook our events - we don't want to create an infinite loop. Control control = Control; - DragEventHandler handler = new DragEventHandler(this.OnDragDrop); + DragEventHandler handler = new DragEventHandler(OnDragDrop); control.DragDrop -= handler; ((IDropTarget)Control).OnDragDrop(de); control.DragDrop += handler; @@ -1203,7 +1264,7 @@ protected virtual void OnDragLeave(EventArgs e) { // unhook our events - we don't want to create an infinite loop. Control control = Control; - EventHandler handler = new EventHandler(this.OnDragLeave); + EventHandler handler = new EventHandler(OnDragLeave); control.DragLeave -= handler; ((IDropTarget)Control).OnDragLeave(e); control.DragLeave += handler; @@ -1216,39 +1277,34 @@ protected virtual void OnDragOver(DragEventArgs de) { // unhook our events - we don't want to create an infinite loop. Control control = Control; - DragEventHandler handler = new DragEventHandler(this.OnDragOver); + DragEventHandler handler = new DragEventHandler(OnDragOver); control.DragOver -= handler; ((IDropTarget)Control).OnDragOver(de); control.DragOver += handler; } /// - /// Event handler for our GiveFeedback event, which is called when a drag operation - /// is in progress. The host will call us with - /// this when an OLE drag event happens. + /// Event handler for our GiveFeedback event, which is called when a drag operation is in progress. The host will call us with this when an OLE drag event happens. /// protected virtual void OnGiveFeedback(GiveFeedbackEventArgs e) { } /// - /// Called in response to the left mouse button being pressed on a - /// component. It ensures that the component is selected. + /// Called in response to the left mouse button being pressed on a component. It ensures that the component is selected. /// protected virtual void OnMouseDragBegin(int x, int y) { - // Ignore another mouse down if we are already in a drag - if (BehaviorService == null && mouseDragLast != InvalidPoint) + // Ignore another mouse down if we are already in a drag. + if (BehaviorService == null && _mouseDragLast != InvalidPoint) { return; } - - mouseDragLast = new Point(x, y); - ctrlSelect = (Control.ModifierKeys & Keys.Control) != 0; + _mouseDragLast = new Point(x, y); + _ctrlSelect = (Control.ModifierKeys & Keys.Control) != 0; ISelectionService sel = (ISelectionService)GetService(typeof(ISelectionService)); - // If the CTRL key isn't down, select this component, otherwise, we wait until the mouse up - // Make sure the component is selected - if (!ctrlSelect && sel != null) + // If the CTRL key isn't down, select this component, otherwise, we wait until the mouse up. Make sure the component is selected + if (!_ctrlSelect && sel != null) { sel.SetSelectedComponents(new object[] { Component }, SelectionTypes.Primary); } @@ -1256,14 +1312,13 @@ protected virtual void OnMouseDragBegin(int x, int y) } /// - /// Called at the end of a drag operation. This either commits or rolls back the - /// drag. + /// Called at the end of a drag operation. This either commits or rolls back the drag. /// protected virtual void OnMouseDragEnd(bool cancel) { - mouseDragLast = InvalidPoint; + _mouseDragLast = InvalidPoint; Control.Capture = false; - if (!mouseDragMoved) + if (!_mouseDragMoved) { // HACK HACK HACK // ParentControlDesigner.Dispose depends on cancel having this behavior. @@ -1271,67 +1326,65 @@ protected virtual void OnMouseDragEnd(bool cancel) { ISelectionService sel = (ISelectionService)GetService(typeof(ISelectionService)); bool shiftSelect = (Control.ModifierKeys & Keys.Shift) != 0; - if (!shiftSelect && (ctrlSelect || (sel != null && !sel.GetComponentSelected(Component)))) + if (!shiftSelect && (_ctrlSelect || (sel != null && !sel.GetComponentSelected(Component)))) { if (sel != null) { sel.SetSelectedComponents(new object[] { Component }, SelectionTypes.Primary); } - ctrlSelect = false; + _ctrlSelect = false; } } return; } - mouseDragMoved = false; - ctrlSelect = false; + _mouseDragMoved = false; + _ctrlSelect = false; // And now finish the drag. if (BehaviorService != null && BehaviorService.Dragging && cancel) { BehaviorService.CancelDrag = true; } // Leave this here in case we are doing a ComponentTray drag - if (selectionUISvc == null) + if (_selectionUISvc == null) { - selectionUISvc = (ISelectionUIService)GetService(typeof(ISelectionUIService)); + _selectionUISvc = (ISelectionUIService)GetService(typeof(ISelectionUIService)); } - if (selectionUISvc == null) + if (_selectionUISvc == null) { return; } // We must check to ensure that UI service is still in drag mode. It is possible that the user hit escape, which will cancel drag mode. - if (selectionUISvc.Dragging) + if (_selectionUISvc.Dragging) { - selectionUISvc.EndDrag(cancel); + _selectionUISvc.EndDrag(cancel); } } /// - /// Called for each movement of the mouse. This will check to see if a drag operation - /// is in progress. If so, it will pass the updated drag dimensions on to the selection - /// UI service. + /// Called for each movement of the mouse. This will check to see if a drag operation is in progress. If so, it will pass the updated drag dimensions on to the selection UI service. /// protected virtual void OnMouseDragMove(int x, int y) { - if (!mouseDragMoved) + if (!_mouseDragMoved) { Size minDrag = SystemInformation.DragSize; Size minDblClick = SystemInformation.DoubleClickSize; minDrag.Width = Math.Max(minDrag.Width, minDblClick.Width); minDrag.Height = Math.Max(minDrag.Height, minDblClick.Height); // we have to make sure the mouse moved farther than the minimum drag distance before we actually start the drag - if (mouseDragLast == InvalidPoint || - (Math.Abs(mouseDragLast.X - x) < minDrag.Width && - Math.Abs(mouseDragLast.Y - y) < minDrag.Height)) + if (_mouseDragLast == InvalidPoint || + (Math.Abs(_mouseDragLast.X - x) < minDrag.Width && + Math.Abs(_mouseDragLast.Y - y) < minDrag.Height)) { return; } else { - mouseDragMoved = true; + _mouseDragMoved = true; // we're on the move, so we're not in a ctrlSelect - ctrlSelect = false; + _ctrlSelect = false; } } @@ -1363,7 +1416,7 @@ protected virtual void OnMouseDragMove(int x, int y) continue;//mixed selection of different parents - don't add this } - if (host.GetDesigner(comp) is ControlDesigner des && (des.SelectionRules & SelectionRules.Moveable) != 0) + if (_host.GetDesigner(comp) is ControlDesigner des && (des.SelectionRules & SelectionRules.Moveable) != 0) { dragControls.Add(comp); } @@ -1375,19 +1428,18 @@ protected virtual void OnMouseDragMove(int x, int y) { using (Graphics adornerGraphics = BehaviorService.AdornerWindowGraphics) { - DropSourceBehavior dsb = new DropSourceBehavior(dragControls, Control.Parent, mouseDragLast); + DropSourceBehavior dsb = new DropSourceBehavior(dragControls, Control.Parent, _mouseDragLast); BehaviorService.DoDragDrop(dsb); } } } - mouseDragLast = InvalidPoint; - mouseDragMoved = false; + _mouseDragLast = InvalidPoint; + _mouseDragMoved = false; } /// - /// Called when the mouse first enters the control. This is forwarded to the parent - /// designer to enable the container selector. + /// Called when the mouse first enters the control. This is forwarded to the parent designer to enable the container selector. /// protected virtual void OnMouseEnter() { @@ -1399,7 +1451,7 @@ protected virtual void OnMouseEnter() parent = parent.Parent; if (parent != null) { - object d = host.GetDesigner(parent); + object d = _host.GetDesigner(parent); if (d != this) { parentDesigner = d; @@ -1414,6 +1466,7 @@ protected virtual void OnMouseEnter() } /// + /// Called after the mouse hovers over the control. This is forwarded to the parent designer to enabled the container selector. /// Called after the mouse hovers over the control. This is forwarded to the parent /// designer to enabled the container selector. /// @@ -1427,7 +1480,7 @@ protected virtual void OnMouseHover() parent = parent.Parent; if (parent != null) { - object d = host.GetDesigner(parent); + object d = _host.GetDesigner(parent); if (d != this) { parentDesigner = d; @@ -1442,8 +1495,7 @@ protected virtual void OnMouseHover() } /// - /// Called when the mouse first enters the control. This is forwarded to the parent - /// designer to enable the container selector. + /// Called when the mouse first enters the control. This is forwarded to the parent designer to enable the container selector. /// protected virtual void OnMouseLeave() { @@ -1455,7 +1507,7 @@ protected virtual void OnMouseLeave() parent = parent.Parent; if (parent != null) { - object d = host.GetDesigner(parent); + object d = _host.GetDesigner(parent); if (d != this) { parentDesigner = d; @@ -1470,31 +1522,24 @@ protected virtual void OnMouseLeave() } /// - /// Called when the control we're designing has finished painting. This method - /// gives the designer a chance to paint any additional adornments on top of the - /// control. + /// Called when the control we're designing has finished painting. This method gives the designer a chance to paint any additional adornments on top of the control. /// protected virtual void OnPaintAdornments(PaintEventArgs pe) { // If this control is being inherited, paint it - if (inheritanceUI != null && pe.ClipRectangle.IntersectsWith(inheritanceUI.InheritanceGlyphRectangle)) + if (_inheritanceUI != null && pe.ClipRectangle.IntersectsWith(_inheritanceUI.InheritanceGlyphRectangle)) { - pe.Graphics.DrawImage(inheritanceUI.InheritanceGlyph, 0, 0); + pe.Graphics.DrawImage(_inheritanceUI.InheritanceGlyph, 0, 0); } } /// - /// Called each time the cursor needs to be set. The ControlDesigner behavior here - /// will set the cursor to one of three things: - /// 1. If the toolbox service has a tool selected, it will allow the toolbox service to - /// set the cursor. - /// 2. If the selection UI service shows a locked selection, or if there is no location - /// property on the control, then the default arrow will be set. - /// 3. Otherwise, the four headed arrow will be set to indicate that the component can - /// be clicked and moved. - /// 4. If the user is currently dragging a component, the crosshair cursor will be used - /// instead of the four headed arrow. + /// Called each time the cursor needs to be set. The ControlDesigner behavior here will set the cursor to one of three things: + /// 1. If the toolbox service has a tool selected, it will allow the toolbox service to set the cursor. + /// 2. If the selection UI service shows a locked selection, or if there is no location property on the control, then the default arrow will be set. + /// 3. Otherwise, the four headed arrow will be set to indicate that the component can be clicked and moved. + /// 4. If the user is currently dragging a component, the crosshair cursor will be used instead of the four headed arrow. /// protected virtual void OnSetCursor() { @@ -1504,29 +1549,29 @@ protected virtual void OnSetCursor() } else { - if (toolboxSvc == null) + if (_toolboxSvc == null) { - toolboxSvc = (IToolboxService)GetService(typeof(IToolboxService)); + _toolboxSvc = (IToolboxService)GetService(typeof(IToolboxService)); } - if (toolboxSvc != null && toolboxSvc.SetCursor()) + if (_toolboxSvc != null && _toolboxSvc.SetCursor()) { return; } - if (!locationChecked) + if (!_locationChecked) { - locationChecked = true; + _locationChecked = true; try { - hasLocation = TypeDescriptor.GetProperties(Component)["Location"] != null; + _hasLocation = TypeDescriptor.GetProperties(Component)["Location"] != null; } catch { } } - if (!hasLocation) + if (!_hasLocation) { Cursor.Current = Cursors.Default; return; @@ -1541,14 +1586,14 @@ protected virtual void OnSetCursor() } } + private bool Locked + { + get => _locked; + set => _locked = value; + } + /// - /// Allows a designer to filter the set of properties - /// the component it is designing will expose through the - /// TypeDescriptor object. This method is called - /// immediately before its corresponding "Post" method. - /// If you are overriding this method you should call - /// the base implementation before you perform your own - /// filtering. + /// Allows a designer to filter the set of properties the component it is designing will expose through the TypeDescriptor object. This method is called immediately before its corresponding "Post" method. If you are overriding this method you should call the base implementation before you perform your own filtering. /// protected override void PreFilterProperties(IDictionary properties) { @@ -1588,15 +1633,13 @@ protected override void PreFilterProperties(IDictionary properties) } /// - /// Hooks the children of the given control. We need to do this for - /// child controls that are not in design mode, which is the case - /// for composite controls. + /// Hooks the children of the given control. We need to do this for child controls that are not in design mode, which is the case for composite controls. /// protected void UnhookChildControls(Control firstChild) { - if (host == null) + if (_host == null) { - host = (IDesignerHost)GetService(typeof(IDesignerHost)); + _host = (IDesignerHost)GetService(typeof(IDesignerHost)); } foreach (Control child in firstChild.Controls) @@ -1619,9 +1662,7 @@ protected void UnhookChildControls(Control firstChild) } /// - /// This method should be called by the extending designer for each message - /// the control would normally receive. This allows the designer to pre-process - /// messages before allowing them to be routed to the control. + /// This method should be called by the extending designer for each message the control would normally receive. This allows the designer to pre-process messages before allowing them to be routed to the control. /// protected virtual void WndProc(ref Message m) { @@ -1629,35 +1670,36 @@ protected virtual void WndProc(ref Message m) // We look at WM_NCHITTEST to determine if the mouse is in a live region of the control if (m.Msg == NativeMethods.WM_NCHITTEST) { - if (!inHitTest) + if (!_inHitTest) { - inHitTest = true; + _inHitTest = true; Point pt = new Point((short)NativeMethods.Util.LOWORD(unchecked((int)(long)m.LParam)), (short)NativeMethods.Util.HIWORD(unchecked((int)(long)m.LParam))); try { - liveRegion = GetHitTest(pt); + _liveRegion = GetHitTest(pt); } catch (Exception e) { - liveRegion = false; + _liveRegion = false; if (ClientUtils.IsCriticalException(e)) { throw; } } - inHitTest = false; + _inHitTest = false; } } // Check to see if the mouse is in a live region of the control and that the context key is not being fired bool isContextKey = (m.Msg == NativeMethods.WM_CONTEXTMENU); - if (liveRegion && (IsMouseMessage(m.Msg) || isContextKey)) + if (_liveRegion && (IsMouseMessage(m.Msg) || isContextKey)) { - // The ActiveX DataGrid control brings up a context menu on right mouse down when it is in edit mode. And, when we generate a WM_CONTEXTMENU message later, it calls DefWndProc() which by default calls the parent (formdesigner). The FormDesigner then brings up the AxHost context menu. This code causes recursive WM_CONTEXTMENU messages to be ignored till we return from the live region message. + // The ActiveX DataGrid control brings up a context menu on right mouse down when it is in edit mode. + // And, when we generate a WM_CONTEXTMENU message later, it calls DefWndProc() which by default calls the parent (formdesigner). The FormDesigner then brings up the AxHost context menu. This code causes recursive WM_CONTEXTMENU messages to be ignored till we return from the live region message. if (m.Msg == NativeMethods.WM_CONTEXTMENU) { - Debug.Assert(!inContextMenu, "Recursively hitting live region for context menu!!!"); - inContextMenu = true; + Debug.Assert(!s_inContextMenu, "Recursively hitting live region for context menu!!!"); + s_inContextMenu = true; } try @@ -1668,13 +1710,14 @@ protected virtual void WndProc(ref Message m) { if (m.Msg == NativeMethods.WM_CONTEXTMENU) { - inContextMenu = false; + s_inContextMenu = false; } if (m.Msg == NativeMethods.WM_LBUTTONUP) { - // DTS SRX040824604234 TabControl loses shortcut menu options after adding ActiveX control. + // terminate the drag. TabControl loses shortcut menu options after adding ActiveX control. OnMouseDragEnd(true); } + } return; } @@ -1683,18 +1726,20 @@ protected virtual void WndProc(ref Message m) int x = 0, y = 0; // Look for a mouse handler. - // CONSIDER - I really don't like this one bit. We need a centralized handler so we can do a global override for the tab order UI, but the designer is a natural fit for an object oriented UI. + // CONSIDER - I really don't like this one bit. We need a + // : centralized handler so we can do a global override for the tab order + // : UI, but the designer is a natural fit for an object oriented UI. if (m.Msg >= NativeMethods.WM_MOUSEFIRST && m.Msg <= NativeMethods.WM_MOUSELAST || m.Msg >= NativeMethods.WM_NCMOUSEMOVE && m.Msg <= NativeMethods.WM_NCMBUTTONDBLCLK || m.Msg == NativeMethods.WM_SETCURSOR) { - if (eventSvc == null) + if (_eventSvc == null) { - eventSvc = (IEventHandlerService)GetService(typeof(IEventHandlerService)); + _eventSvc = (IEventHandlerService)GetService(typeof(IEventHandlerService)); } - if (eventSvc != null) + if (_eventSvc != null) { - mouseHandler = (IMouseHandler)eventSvc.GetHandler(typeof(IMouseHandler)); + mouseHandler = (IMouseHandler)_eventSvc.GetHandler(typeof(IMouseHandler)); } } @@ -1732,7 +1777,6 @@ protected virtual void WndProc(ref Message m) // See "How to Handle WM_GETOBJECT" in MSDN if (NativeMethods.OBJID_CLIENT == unchecked((int)(long)m.LParam)) { - // Get the IAccessible GUID Guid IID_IAccessible = new Guid(NativeMethods.uuid_IAccessible); // Get an Lresult for the accessibility Object for this control IntPtr punkAcc; @@ -1806,7 +1850,6 @@ protected virtual void WndProc(ref Message m) { button = MouseButtons.Left; } - if (button == MouseButtons.Left) { // We handle doubleclick messages, and we also process our own simulated double clicks for controls that don't specify CS_WANTDBLCLKS. @@ -1848,26 +1891,27 @@ protected virtual void WndProc(ref Message m) } else { - toolPassThrough = false; + _toolPassThrough = false; if (!EnableDragRect && button == MouseButtons.Left) { - if (toolboxSvc == null) + if (_toolboxSvc == null) { - toolboxSvc = (IToolboxService)GetService(typeof(IToolboxService)); + _toolboxSvc = (IToolboxService)GetService(typeof(IToolboxService)); } - if (toolboxSvc != null && toolboxSvc.GetSelectedToolboxItem((IDesignerHost)GetService(typeof(IDesignerHost))) != null) + if (_toolboxSvc != null && _toolboxSvc.GetSelectedToolboxItem((IDesignerHost)GetService(typeof(IDesignerHost))) != null) { // there is a tool to be dragged, so set passthrough and pass to the parent. - toolPassThrough = true; + _toolPassThrough = true; } } else { - toolPassThrough = false; + _toolPassThrough = false; } - if (toolPassThrough) + + if (_toolPassThrough) { NativeMethods.SendMessage(Control.Parent.Handle, m.Msg, m.WParam, (IntPtr)GetParentPointFromLparam(m.LParam)); return; @@ -1880,6 +1924,7 @@ protected virtual void WndProc(ref Message m) else if (button == MouseButtons.Left) { OnMouseDragBegin(x, y); + } else if (button == MouseButtons.Right) { @@ -1889,10 +1934,11 @@ protected virtual void WndProc(ref Message m) selSvc.SetSelectedComponents(new object[] { Component }, SelectionTypes.Primary); } } - lastMoveScreenX = x; - lastMoveScreenY = y; + _lastMoveScreenX = x; + _lastMoveScreenY = y; } break; + case NativeMethods.WM_NCMOUSEMOVE: case NativeMethods.WM_MOUSEMOVE: if ((unchecked((int)(long)m.WParam) & NativeMethods.MK_LBUTTON) != 0) @@ -1902,16 +1948,16 @@ protected virtual void WndProc(ref Message m) else if ((unchecked((int)(long)m.WParam) & NativeMethods.MK_RBUTTON) != 0) { button = MouseButtons.Right; - toolPassThrough = false; + _toolPassThrough = false; } else { - toolPassThrough = false; + _toolPassThrough = false; } - if (lastMoveScreenX != x || lastMoveScreenY != y) + if (_lastMoveScreenX != x || _lastMoveScreenY != y) { - if (toolPassThrough) + if (_toolPassThrough) { NativeMethods.SendMessage(Control.Parent.Handle, m.Msg, m.WParam, (IntPtr)GetParentPointFromLparam(m.LParam)); return; @@ -1926,9 +1972,10 @@ protected virtual void WndProc(ref Message m) OnMouseDragMove(x, y); } } - lastMoveScreenX = x; - lastMoveScreenY = y; - // We eat WM_NCMOUSEMOVE messages, since we don't want the non-client area of design time controls to repaint on mouse move. + _lastMoveScreenX = x; + _lastMoveScreenY = y; + + // We eat WM_NCMOUSEMOVE messages, since we don't want the non-client area/ of design time controls to repaint on mouse move. if (m.Msg == NativeMethods.WM_MOUSEMOVE) { BaseWndProc(ref m); @@ -1955,10 +2002,10 @@ protected virtual void WndProc(ref Message m) } else { - if (toolPassThrough) + if (_toolPassThrough) { NativeMethods.SendMessage(Control.Parent.Handle, m.Msg, m.WParam, (IntPtr)GetParentPointFromLparam(m.LParam)); - toolPassThrough = false; + _toolPassThrough = false; return; } @@ -1967,9 +2014,8 @@ protected virtual void WndProc(ref Message m) OnMouseDragEnd(false); } } - // clear any pass through. - toolPassThrough = false; + _toolPassThrough = false; BaseWndProc(ref m); break; case NativeMethods.WM_PRINTCLIENT: @@ -1984,20 +2030,17 @@ protected virtual void WndProc(ref Message m) } } break; - case NativeMethods.WM_PAINT: // First, save off the update region and call our base class. if (OleDragDropHandler.FreezePainting) { - NativeMethods.ValidateRect(m.HWnd, IntPtr.Zero); + SafeNativeMethods.ValidateRect(m.HWnd, IntPtr.Zero); break; } - if (Control == null) { break; } - NativeMethods.RECT clip = new NativeMethods.RECT(); IntPtr hrgn = NativeMethods.CreateRectRgn(0, 0, 0, 0); NativeMethods.GetUpdateRgn(m.HWnd, hrgn, false); @@ -2007,7 +2050,7 @@ protected virtual void WndProc(ref Message m) try { // Call the base class to do its own painting. - if (thrownException == null) + if (_thrownException == null) { DefWndProc(ref m); } @@ -2019,28 +2062,30 @@ protected virtual void WndProc(ref Message m) if (m.HWnd != Control.Handle) { // Re-map the clip rect we pass to the paint event args to our child coordinates. - NativeMethods.POINT pt = new NativeMethods.POINT(); - pt.x = 0; - pt.y = 0; + NativeMethods.POINT pt = new NativeMethods.POINT + { + x = 0, + y = 0 + }; NativeMethods.MapWindowPoints(m.HWnd, Control.Handle, pt, 1); gr.TranslateTransform(-pt.x, -pt.y); NativeMethods.MapWindowPoints(m.HWnd, Control.Handle, ref clip, 2); } - paintRect = new Rectangle(clip.left, clip.top, clip.right - clip.left, clip.bottom - clip.top); PaintEventArgs pevent = new PaintEventArgs(gr, paintRect); + try { gr.Clip = r; - if (thrownException == null) + if (_thrownException == null) { OnPaintAdornments(pevent); } else { - UnsafeNativeMethods.PAINTSTRUCT ps = new UnsafeNativeMethods.PAINTSTRUCT(); + NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT(); UnsafeNativeMethods.BeginPaint(m.HWnd, ref ps); - PaintException(pevent, thrownException); + PaintException(pevent, _thrownException); UnsafeNativeMethods.EndPaint(m.HWnd, ref ps); } } @@ -2062,7 +2107,7 @@ protected virtual void WndProc(ref Message m) if (OverlayService != null) { - //this will allow any Glyphs to re-paint after this control and its designer has painted + // this will allow any Glyphs to re-paint after this control and its designer has painted paintRect.Location = Control.PointToScreen(paintRect.Location); OverlayService.InvalidateOverlays(paintRect); } @@ -2073,13 +2118,12 @@ protected virtual void WndProc(ref Message m) { DefWndProc(ref m); } - else if (thrownException == null) + else if (_thrownException == null) { DefWndProc(ref m); } // For some reason we dont always get an NCPAINT with the WM_NCACTIVATE usually this repros with themes on.... this can happen when someone calls RedrawWindow without the flags to send an NCPAINT. So that we dont double process this event, our calls to redraw window should not have RDW_ERASENOW | RDW_UPDATENOW. - if (OverlayService != null) { if (Control != null && Control.Size != Control.ClientSize && Control.Parent != null) @@ -2099,7 +2143,9 @@ protected virtual void WndProc(ref Message m) case NativeMethods.WM_SETCURSOR: // We always handle setting the cursor ourselves. - if (liveRegion) + // + + if (_liveRegion) { DefWndProc(ref m); break; @@ -2115,7 +2161,7 @@ protected virtual void WndProc(ref Message m) } break; case NativeMethods.WM_SIZE: - if (this.thrownException != null) + if (_thrownException != null) { Control.Invalidate(); } @@ -2129,13 +2175,14 @@ protected virtual void WndProc(ref Message m) case NativeMethods.WM_SETFOCUS: // We eat the focus unless the target is a ToolStrip edit node (TransparentToolStrip). If we eat the focus in that case, the Windows Narrator won't follow navigation via the keyboard. // NB: "ToolStrip" is a bit of a misnomer here, because the ToolStripTemplateNode is also used for MenuStrip, StatusStrip, etc... - if (Control.FromHandle(m.HWnd) is ToolStripTemplateNode.TransparentToolStrip) - { - DefWndProc(ref m); - } - else if (host != null && host.RootComponent != null) - { - if (host.GetDesigner(host.RootComponent) is IRootDesigner rd) + //if (Control.FromHandle(m.HWnd) is ToolStripTemplateNode.TransparentToolStrip) + //{ + // DefWndProc(ref m); + //} + //else + if (_host != null && _host.RootComponent != null) + { + if (_host.GetDesigner(_host.RootComponent) is IRootDesigner rd) { ViewTechnology[] techs = rd.SupportedTechnologies; if (techs.Length > 0) @@ -2149,10 +2196,11 @@ protected virtual void WndProc(ref Message m) } break; case NativeMethods.WM_CONTEXTMENU: - if (inContextMenu) + if (s_inContextMenu) { break; } + // We handle this in addition to a right mouse button. Why? Because we often eat the right mouse button, so it may never generate a WM_CONTEXTMENU. However, the system may generate one in response to an F-10. x = NativeMethods.Util.SignedLOWORD(unchecked((int)(long)m.LParam)); y = NativeMethods.Util.SignedHIWORD(unchecked((int)(long)m.LParam)); @@ -2191,18 +2239,190 @@ protected virtual void WndProc(ref Message m) } } - [ComVisible(true)] - public class ControlDesignerAccessibleObject : AccessibleObject + private void PaintException(PaintEventArgs e, Exception ex) { - private readonly ControlDesigner _designer = null; - private Control _control = null; - private IDesignerHost _host = null; - private ISelectionService _selSvc = null; + StringFormat stringFormat = new StringFormat + { + Alignment = StringAlignment.Near, + LineAlignment = StringAlignment.Near + }; + string exceptionText = ex.ToString(); + stringFormat.SetMeasurableCharacterRanges(new CharacterRange[] { new CharacterRange(0, exceptionText.Length) }); + + // rendering calculations... + int penThickness = 2; + Size glyphSize = SystemInformation.IconSize; + int marginX = penThickness * 2; + int marginY = penThickness * 2; + + Rectangle clientRectangle = Control.ClientRectangle; + Rectangle borderRectangle = clientRectangle; + borderRectangle.X++; + borderRectangle.Y++; + borderRectangle.Width -= 2; + borderRectangle.Height -= 2; + + Rectangle imageRect = new Rectangle(marginX, marginY, glyphSize.Width, glyphSize.Height); + Rectangle textRect = clientRectangle; + textRect.X = imageRect.X + imageRect.Width + 2 * marginX; + textRect.Y = imageRect.Y; + textRect.Width -= (textRect.X + marginX + penThickness); + textRect.Height -= (textRect.Y + marginY + penThickness); + + using (Font errorFont = new Font(Control.Font.FontFamily, Math.Max(SystemInformation.ToolWindowCaptionHeight - SystemInformation.BorderSize.Height - 2, Control.Font.Height), GraphicsUnit.Pixel)) + { + using (Region textRegion = e.Graphics.MeasureCharacterRanges(exceptionText, errorFont, textRect, stringFormat)[0]) + { + // paint contents... clipping optimizations for less flicker... + Region originalClip = e.Graphics.Clip; + e.Graphics.ExcludeClip(textRegion); + e.Graphics.ExcludeClip(imageRect); + try + { + e.Graphics.FillRectangle(Brushes.White, clientRectangle); + } + finally + { + e.Graphics.Clip = originalClip; + } - public ControlDesignerAccessibleObject(ControlDesigner designer, Control control) - { - _designer = designer; - _control = control; + using (Pen pen = new Pen(Color.Red, penThickness)) + { + e.Graphics.DrawRectangle(pen, borderRectangle); + } + + Icon err = SystemIcons.Error; + e.Graphics.FillRectangle(Brushes.White, imageRect); + e.Graphics.DrawIcon(err, imageRect.X, imageRect.Y); + textRect.X++; + e.Graphics.IntersectClip(textRegion); + try + { + e.Graphics.FillRectangle(Brushes.White, textRect); + e.Graphics.DrawString(exceptionText, errorFont, new SolidBrush(Control.ForeColor), textRect, stringFormat); + } + finally + { + e.Graphics.Clip = originalClip; + } + } + } + stringFormat.Dispose(); + } + + private IOverlayService OverlayService + { + get + { + if (_overlayService == null) + { + _overlayService = (IOverlayService)GetService(typeof(IOverlayService)); + } + return _overlayService; + } + } + + private bool IsMouseMessage(int msg) + { + if (msg >= NativeMethods.WM_MOUSEFIRST && msg <= NativeMethods.WM_MOUSELAST) + { + return true; + } + + switch (msg) + { + // WM messages not covered by the above block + case NativeMethods.WM_MOUSEHOVER: + case NativeMethods.WM_MOUSELEAVE: + // WM_NC messages + case NativeMethods.WM_NCMOUSEMOVE: + case NativeMethods.WM_NCLBUTTONDOWN: + case NativeMethods.WM_NCLBUTTONUP: + case NativeMethods.WM_NCLBUTTONDBLCLK: + case NativeMethods.WM_NCRBUTTONDOWN: + case NativeMethods.WM_NCRBUTTONUP: + case NativeMethods.WM_NCRBUTTONDBLCLK: + case NativeMethods.WM_NCMBUTTONDOWN: + case NativeMethods.WM_NCMBUTTONUP: + case NativeMethods.WM_NCMBUTTONDBLCLK: + case NativeMethods.WM_NCMOUSEHOVER: + case NativeMethods.WM_NCMOUSELEAVE: + case NativeMethods.WM_NCXBUTTONDOWN: + case NativeMethods.WM_NCXBUTTONUP: + case NativeMethods.WM_NCXBUTTONDBLCLK: + return true; + default: + return false; + } + } + + private bool IsDoubleClick(int x, int y) + { + bool doubleClick = false; + int wait = SystemInformation.DoubleClickTime; + int elapsed = SafeNativeMethods.GetTickCount() - _lastClickMessageTime; + if (elapsed <= wait) + { + Size dblClick = SystemInformation.DoubleClickSize; + if (x >= _lastClickMessagePositionX - dblClick.Width + && x <= _lastClickMessagePositionX + dblClick.Width + && y >= _lastClickMessagePositionY - dblClick.Height + && y <= _lastClickMessagePositionY + dblClick.Height) + { + doubleClick = true; + } + } + + if (!doubleClick) + { + _lastClickMessagePositionX = x; + _lastClickMessagePositionY = y; + _lastClickMessageTime = SafeNativeMethods.GetTickCount(); + } + else + { + _lastClickMessagePositionX = _lastClickMessagePositionY = 0; + _lastClickMessageTime = 0; + } + return doubleClick; + } + + private void OnMouseDoubleClick() + { + try + { + DoDefaultAction(); + } + catch (Exception e) + { + DisplayError(e); + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + } + } + + private int GetParentPointFromLparam(IntPtr lParam) + { + Point pt = new Point(NativeMethods.Util.SignedLOWORD(unchecked((int)(long)lParam)), NativeMethods.Util.SignedHIWORD(unchecked((int)(long)lParam))); + pt = Control.PointToScreen(pt); + pt = Control.Parent.PointToClient(pt); + return NativeMethods.Util.MAKELONG(pt.X, pt.Y); + } + + [ComVisible(true)] + public class ControlDesignerAccessibleObject : AccessibleObject + { + private readonly ControlDesigner _designer = null; + private readonly Control _control = null; + private IDesignerHost _host = null; + private ISelectionService _selSvc = null; + + public ControlDesignerAccessibleObject(ControlDesigner designer, Control control) + { + _designer = designer; + _control = control; } public override Rectangle Bounds @@ -2335,170 +2555,492 @@ public override AccessibleObject GetSelected() public override AccessibleObject HitTest(int x, int y) => _control.AccessibilityObject.HitTest(x, y); } - // Custom code dom serializer for the DesignerControlCollection. We need this so we can filter out controls that aren't sited in the host's container. - internal class DesignerControlCollectionCodeDomSerializer : CollectionCodeDomSerializer + /// + /// This TransparentBehavior is associated with the BodyGlyph for this ControlDesigner. When the BehaviorService hittests a glyph w/a TransparentBehavior, all messages will be passed through the BehaviorService directly to the ControlDesigner. During a Drag operation, when the BehaviorService hittests + /// + internal class TransparentBehavior : Behavior.Behavior { - protected override object SerializeCollection(IDesignerSerializationManager manager, CodeExpression targetExpression, Type targetType, ICollection originalCollection, ICollection valuesToSerialize) + readonly ControlDesigner _designer; + Rectangle _controlRect = Rectangle.Empty; + + /// + /// Constructor that accepts the related ControlDesigner. + /// + internal TransparentBehavior(ControlDesigner designer) { - ArrayList subset = new ArrayList(); - if (valuesToSerialize != null && valuesToSerialize.Count > 0) + _designer = designer; + } + + /// + /// This property performs a hit test on the ControlDesigner to determine if the BodyGlyph should return '-1' for hit testing (letting all messages pass directly to the the control). + /// + internal bool IsTransparent(Point p) => _designer.GetHitTest(p); + + /// + /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. + /// + public override void OnDragDrop(Glyph g, DragEventArgs e) + { + _controlRect = Rectangle.Empty; + _designer.OnDragDrop(e); + } + + /// + /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. + /// + public override void OnDragEnter(Glyph g, DragEventArgs e) + { + if (_designer != null && _designer.Control != null) { - foreach (object val in valuesToSerialize) + _controlRect = _designer.Control.RectangleToScreen(_designer.Control.ClientRectangle); + } + _designer.OnDragEnter(e); + } + + /// + /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. + /// + public override void OnDragLeave(Glyph g, EventArgs e) + { + _controlRect = Rectangle.Empty; + _designer.OnDragLeave(e); + } + + /// + /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. + /// + public override void OnDragOver(Glyph g, DragEventArgs e) + { + // If we are not over a valid drop area, then do not allow the drag/drop. Now that all dragging/dropping is done via the behavior service and adorner window, we have to do our own validation, and cannot rely on the OS to do it for us. + if (e != null && _controlRect != Rectangle.Empty && !_controlRect.Contains(new Point(e.X, e.Y))) + { + e.Effect = DragDropEffects.None; + return; + } + _designer.OnDragOver(e); + } + + /// + /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. + /// + public override void OnGiveFeedback(Glyph g, GiveFeedbackEventArgs e) + { + _designer.OnGiveFeedback(e); + } + } + + [SuppressMessage("Microsoft.Performance", "CA1806:DoNotIgnoreMethodResults")] + internal void HookChildHandles(IntPtr firstChild) + { + IntPtr hwndChild = firstChild; + while (hwndChild != IntPtr.Zero) + { + if (!IsWindowInCurrentProcess(hwndChild)) + { + break; + } + + // Is it a control? + Control child = Control.FromHandle(hwndChild); + if (child == null) + { + // No control. We must subclass this control. + if (!SubclassedChildWindows.ContainsKey(hwndChild)) { - if (val is IComponent comp && comp.Site != null && !(comp.Site is INestedSite)) - { - subset.Add(comp); - } + // Some controls (primarily RichEdit) will register themselves as + // drag-drop source/targets when they are instantiated. Since these hwnds do not + // have a Windows Forms control associated with them, we have to RevokeDragDrop() + // for them so that the ParentControlDesigner()'s drag-drop support can work + // correctly. + UnsafeNativeMethods.RevokeDragDrop(hwndChild); + new ChildSubClass(this, hwndChild); + SubclassedChildWindows[hwndChild] = true; } } - return base.SerializeCollection(manager, targetExpression, targetType, originalCollection, subset); + + // UserControl is a special ContainerControl which should "hook to all the WindowHandles" + // Since it doesnt allow the Mouse to pass through any of its contained controls. + // Please refer to VsWhidbey : 293117 + if (child == null || Control is UserControl) + { + // Now do the children of this window. + HookChildHandles(UnsafeNativeMethods.GetWindow(hwndChild, NativeMethods.GW_CHILD)); + } + hwndChild = UnsafeNativeMethods.GetWindow(hwndChild, NativeMethods.GW_HWNDNEXT); } } - [ListBindable(false)] - [DesignerSerializer(typeof(DesignerControlCollectionCodeDomSerializer), typeof(CodeDomSerializer))] - internal class DesignerControlCollection : Control.ControlCollection, IList + private bool IsWindowInCurrentProcess(IntPtr hwnd) { - Control.ControlCollection realCollection; - public DesignerControlCollection(Control owner) : base(owner) + SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(null, hwnd), out int pid); + return pid == CurrentProcessId; + } + + private int CurrentProcessId + { + get { - realCollection = owner.Controls; + if (s_currentProcessId == 0) + { + s_currentProcessId = SafeNativeMethods.GetCurrentProcessId(); + } + return s_currentProcessId; } + } - public override int Count + private void OnHandleChange() + { + // We must now traverse child handles for this control. There are three types of child handles and we are interested in two of them: + // 1. Child handles that do not have a Control associated with them. We must subclass these and prevent them from getting design-time events. + // 2. Child handles that do have a Control associated with them, but the control does not have a designer. We must hook the WindowTarget on these controls and prevent them from getting design-time events. + // 3. Child handles that do have a Control associated with them, and the control has a designer. We ignore these and let the designer handle their messages. + HookChildHandles(UnsafeNativeMethods.GetWindow(Control.Handle, NativeMethods.GW_CHILD)); + HookChildControls(Control); + } + + private class ChildSubClass : NativeWindow, IDesignerTarget + { + private ControlDesigner _designer; + + // AssignHandle calls NativeWindow::OnHandleChanged, but we do not override it so we should be okay + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public ChildSubClass(ControlDesigner designer, IntPtr hwnd) { - get + _designer = designer; + if (designer != null) { - return realCollection.Count; + designer.DisposingHandler += new EventHandler(OnDesignerDisposing); } + AssignHandle(hwnd); } - object ICollection.SyncRoot + void IDesignerTarget.DefWndProc(ref Message m) => base.DefWndProc(ref m); + + public void Dispose() => _designer = null; + + private void OnDesignerDisposing(object sender, EventArgs e) => Dispose(); + + protected override void WndProc(ref Message m) { - get + if (_designer == null) { - return this; + DefWndProc(ref m); + return; + } + + if (m.Msg == NativeMethods.WM_DESTROY) + { + _designer.RemoveSubclassedWindow(m.HWnd); + } + if (m.Msg == NativeMethods.WM_PARENTNOTIFY && NativeMethods.Util.LOWORD(unchecked((int)(long)m.WParam)) == (short)NativeMethods.WM_CREATE) + { + _designer.HookChildHandles(m.LParam); // they will get removed from the collection just above + } + + // We want these messages to go through the designer's WndProc method, and we want people to be able to do default processing with the designer's DefWndProc. So, we stuff ourselves into the designers window target and call their WndProc. + IDesignerTarget designerTarget = _designer.DesignerTarget; + _designer.DesignerTarget = this; + Debug.Assert(m.HWnd == Handle, "Message handle differs from target handle"); + try + { + _designer.WndProc(ref m); + } + catch (Exception ex) + { + _designer.SetUnhandledException(Control.FromChildHandle(m.HWnd), ex); + } + finally + { + // make sure the designer wasn't destroyed + if (_designer != null && _designer.Component != null) + { + _designer.DesignerTarget = designerTarget; + } } } + } - bool ICollection.IsSynchronized + internal void RemoveSubclassedWindow(IntPtr hwnd) + { + if (SubclassedChildWindows.ContainsKey(hwnd)) { - get + SubclassedChildWindows.Remove(hwnd); + } + } + + private class DesignerWindowTarget : IWindowTarget, IDesignerTarget, IDisposable + { + internal ControlDesigner _designer; + internal IWindowTarget _oldTarget; + + public DesignerWindowTarget(ControlDesigner designer) + { + Control control = designer.Control; + _designer = designer; + _oldTarget = control.WindowTarget; + control.WindowTarget = this; + } + + public void DefWndProc(ref Message m) + { + _oldTarget.OnMessage(ref m); + } + + public void Dispose() + { + if (_designer != null) { - return false; + _designer.Control.WindowTarget = _oldTarget; + _designer = null; } } - bool IList.IsFixedSize + public void OnHandleChange(IntPtr newHandle) { - get + _oldTarget.OnHandleChange(newHandle); + if (newHandle != IntPtr.Zero) { - return false; + _designer.OnHandleChange(); } } - public new bool IsReadOnly + public void OnMessage(ref Message m) { - get + // We want these messages to go through the designer's WndProc method, and we want people to be able to do default processing with the designer's DefWndProc. So, we stuff ourselves into the designers window target and call their WndProc. + ControlDesigner currentDesigner = _designer; + if (currentDesigner != null) { - return realCollection.IsReadOnly; + IDesignerTarget designerTarget = currentDesigner.DesignerTarget; + currentDesigner.DesignerTarget = this; + try + { + currentDesigner.WndProc(ref m); + } + catch (Exception ex) + { + currentDesigner.SetUnhandledException(currentDesigner.Control, ex); + } + finally + { + currentDesigner.DesignerTarget = designerTarget; + } + } + else + { + DefWndProc(ref m); } } + } - int IList.Add(object control) + private class ChildWindowTarget : IWindowTarget, IDesignerTarget + { + private readonly ControlDesigner _designer; + private readonly Control _childControl; + private readonly IWindowTarget _oldWindowTarget; + private IntPtr _handle = IntPtr.Zero; + + public ChildWindowTarget(ControlDesigner designer, Control childControl, IWindowTarget oldWindowTarget) { - return ((IList)realCollection).Add(control); + _designer = designer; + _childControl = childControl; + _oldWindowTarget = oldWindowTarget; } - public override void Add(Control c) + public IWindowTarget OldWindowTarget { - realCollection.Add(c); + get => _oldWindowTarget; } - public override void AddRange(Control[] controls) + public void DefWndProc(ref Message m) => _oldWindowTarget.OnMessage(ref m); + + [SuppressMessage("Microsoft.Usage", "CA2216:DisposableTypesShouldDeclareFinalizer")] + public void Dispose() { - realCollection.AddRange(controls); + // Do nothing. We will pick this up through a null DesignerTarget property when we come out of the message loop. } - bool IList.Contains(object control) + public void OnHandleChange(IntPtr newHandle) { - return ((IList)realCollection).Contains(control); + _handle = newHandle; + _oldWindowTarget.OnHandleChange(newHandle); } - public new void CopyTo(Array dest, int index) + public void OnMessage(ref Message m) { - realCollection.CopyTo(dest, index); + // If the designer has jumped ship, the continue partying on messages, but send them back to the original control. + if (_designer.Component == null) + { + _oldWindowTarget.OnMessage(ref m); + return; + } + + // We want these messages to go through the designer's WndProc method, and we want people to be able to do default processing with the designer's DefWndProc. So, we stuff the old window target into the designer's target and then call their WndProc. + IDesignerTarget designerTarget = _designer.DesignerTarget; + _designer.DesignerTarget = this; + + try + { + _designer.WndProc(ref m); + } + catch (Exception ex) + { + _designer.SetUnhandledException(_childControl, ex); + } + finally + { + // If the designer disposed us, then we should follow suit. + if (_designer.DesignerTarget == null) + { + designerTarget.Dispose(); + } + else + { + _designer.DesignerTarget = designerTarget; + } + + // Controls (primarily RichEdit) will register themselves as drag-drop source/targets when they are instantiated. Normally, when they are being designed, we will RevokeDragDrop() in their designers. The problem occurs when these controls are inside a UserControl. At that time, we do not have a designer for these controls, and they prevent the ParentControlDesigner's drag-drop from working. What we do is to loop through all child controls that do not have a designer (in HookChildControls()), and RevokeDragDrop() after their handles have been created. + if (m.Msg == NativeMethods.WM_CREATE) + { + Debug.Assert(_handle != IntPtr.Zero, "Handle for control not created"); + UnsafeNativeMethods.RevokeDragDrop(_handle); + } + } } + } - public override bool Equals(object other) + internal void SetUnhandledException(Control owner, Exception exception) + { + if (_thrownException == null) { - return realCollection.Equals(other); + _thrownException = exception; + if (owner == null) + { + owner = Control; + } + string stack = string.Empty; + string[] exceptionLines = exception.StackTrace.Split('\r', '\n'); + string typeName = owner.GetType().FullName; + foreach (string line in exceptionLines) + { + if (line.IndexOf(typeName) != -1) + { + stack = string.Format(CultureInfo.CurrentCulture, "{0}\r\n{1}", stack, line); + } + } + + Exception wrapper = new Exception(string.Format(SR.ControlDesigner_WndProcException, typeName, exception.Message, stack), exception); + DisplayError(wrapper); + // hide all the child controls. + foreach (Control c in Control.Controls) + { + c.Visible = false; + } + Control.Invalidate(true); } + } - public new IEnumerator GetEnumerator() + [ListBindable(false)] + [DesignerSerializer(typeof(DesignerControlCollectionCodeDomSerializer), typeof(CodeDomSerializer))] + internal class DesignerControlCollection : Control.ControlCollection, IList + { + readonly Control.ControlCollection _realCollection; + public DesignerControlCollection(Control owner) : base(owner) { - return realCollection.GetEnumerator(); + _realCollection = owner.Controls; } - public override int GetHashCode() + public override int Count { - return realCollection.GetHashCode(); + get => _realCollection.Count; } - int IList.IndexOf(object control) + object ICollection.SyncRoot { - return ((IList)realCollection).IndexOf(control); + get => this; } - void IList.Insert(int index, object value) + bool ICollection.IsSynchronized { - ((IList)realCollection).Insert(index, value); + get => false; } - void IList.Remove(object control) + bool IList.IsFixedSize { - ((IList)realCollection).Remove(control); + get => false; } - void IList.RemoveAt(int index) + public new bool IsReadOnly { - ((IList)realCollection).RemoveAt(index); + get => _realCollection.IsReadOnly; } + int IList.Add(object control) => ((IList)_realCollection).Add(control); + + public override void Add(Control c) => _realCollection.Add(c); + + public override void AddRange(Control[] controls) => _realCollection.AddRange(controls); + + bool IList.Contains(object control) => ((IList)_realCollection).Contains(control); + + public new void CopyTo(Array dest, int index) => _realCollection.CopyTo(dest, index); + + public override bool Equals(object other) => _realCollection.Equals(other); + + public new IEnumerator GetEnumerator() => _realCollection.GetEnumerator(); + + public override int GetHashCode() => _realCollection.GetHashCode(); + + int IList.IndexOf(object control) => ((IList)_realCollection).IndexOf(control); + + void IList.Insert(int index, object value) => ((IList)_realCollection).Insert(index, value); + + void IList.Remove(object control) => ((IList)_realCollection).Remove(control); + + void IList.RemoveAt(int index) => ((IList)_realCollection).RemoveAt(index); + object IList.this[int index] { - get - { - return ((IList)realCollection)[index]; - } - set - { - throw new NotSupportedException(); - } + get => ((IList)_realCollection)[index]; + set => throw new NotSupportedException(); } - public override int GetChildIndex(Control child, bool throwException) - { - return realCollection.GetChildIndex(child, throwException); - } + public override int GetChildIndex(Control child, bool throwException) => _realCollection.GetChildIndex(child, throwException); // we also need to redirect this guy - public override void SetChildIndex(Control child, int newIndex) + public override void SetChildIndex(Control child, int newIndex) => _realCollection.SetChildIndex(child, newIndex); + + public override void Clear() { - realCollection.SetChildIndex(child, newIndex); + for (int i = _realCollection.Count - 1; i >= 0; i--) + { + if (_realCollection[i] != null && + _realCollection[i].Site != null && + TypeDescriptor.GetAttributes(_realCollection[i]).Contains(InheritanceAttribute.NotInherited)) + { + _realCollection.RemoveAt(i); + } + } } + } - public override void Clear() + // Custom code dom serializer for the DesignerControlCollection. We need this so we can filter out controls + // that aren't sited in the host's container. + internal class DesignerControlCollectionCodeDomSerializer : CollectionCodeDomSerializer + { + protected override object SerializeCollection(IDesignerSerializationManager manager, CodeExpression targetExpression, Type targetType, ICollection originalCollection, ICollection valuesToSerialize) { - for (int i = realCollection.Count - 1; i >= 0; i--) + ArrayList subset = new ArrayList(); + if (valuesToSerialize != null && valuesToSerialize.Count > 0) { - if (realCollection[i] != null && - realCollection[i].Site != null && - TypeDescriptor.GetAttributes(realCollection[i]).Contains(InheritanceAttribute.NotInherited)) + foreach (object val in valuesToSerialize) { - realCollection.RemoveAt(i); + if (val is IComponent comp && comp.Site != null && !(comp.Site is INestedSite)) + { + subset.Add(comp); + } } } + return base.SerializeCollection(manager, targetExpression, targetType, originalCollection, subset); } } @@ -2506,6 +3048,7 @@ private class DockingActionList : DesignerActionList { private readonly ControlDesigner _designer; private readonly IDesignerHost _host; + public DockingActionList(ControlDesigner owner) : base(owner.Component) { _designer = owner; @@ -2564,75 +3107,48 @@ private void OnDockActionClick(object sender, EventArgs e) } } - /// - /// This TransparentBehavior is associated with the BodyGlyph for this ControlDesigner. When the BehaviorService hittests a glyph w/a TransparentBehavior, all messages will be passed through the BehaviorService directly to the ControlDesigner. During a Drag operation, when the BehaviorService hittests - /// - internal class TransparentBehavior : Behavior.Behavior + private class CanResetSizePropertyDescriptor : PropertyDescriptor { - /// - /// Constructor that accepts the related ControlDesigner. - /// - internal TransparentBehavior(ControlDesigner designer) - { - this.designer = designer; - } + private readonly PropertyDescriptor _basePropDesc; - /// - /// This property performs a hit test on the ControlDesigner to determine if the BodyGlyph should return '-1' for hit testing (letting all messages pass directly to the the control). - /// - internal bool IsTransparent(Point p) => designer.GetHitTest(p); - - /// - /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. - /// - public override void OnDragDrop(Glyph g, DragEventArgs e) + public CanResetSizePropertyDescriptor(PropertyDescriptor pd) : base(pd) { - controlRect = Rectangle.Empty; - designer.OnDragDrop(e); + _basePropDesc = pd; } - /// - /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. - /// - public override void OnDragEnter(Glyph g, DragEventArgs e) + public override Type ComponentType { - if (designer != null && designer.Control != null) - { - controlRect = designer.Control.RectangleToScreen(designer.Control.ClientRectangle); - } - designer.OnDragEnter(e); + get => _basePropDesc.ComponentType; } - /// - /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. - /// - public override void OnDragLeave(Glyph g, EventArgs e) + public override string DisplayName { - controlRect = Rectangle.Empty; - designer.OnDragLeave(e); + get => _basePropDesc.DisplayName; } - /// - /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. - /// - public override void OnDragOver(Glyph g, DragEventArgs e) + public override bool IsReadOnly { - // If we are not over a valid drop area, then do not allow the drag/drop. Now that all dragging/dropping is done via the behavior service and adorner window, we have to do our own validation, and cannot rely on the OS to do it for us. - if (e != null && controlRect != Rectangle.Empty && !controlRect.Contains(new Point(e.X, e.Y))) - { - e.Effect = DragDropEffects.None; - return; - } - designer.OnDragOver(e); + get => _basePropDesc.IsReadOnly; } - /// - /// Forwards DragDrop notification from the BehaviorService to the related ControlDesigner. - /// - public override void OnGiveFeedback(Glyph g, GiveFeedbackEventArgs e) + public override Type PropertyType { - designer.OnGiveFeedback(e); + get => _basePropDesc.PropertyType; } + + + // since we can't get to the DefaultSize property, we use the existing ShouldSerialize logic. + public override bool CanResetValue(object component) => _basePropDesc.ShouldSerializeValue(component); + + public override object GetValue(object component) => _basePropDesc.GetValue(component); + + public override void ResetValue(object component) => _basePropDesc.ResetValue(component); + + + public override void SetValue(object component, object value) => _basePropDesc.SetValue(component, value); + + // we always want to serialize values. + public override bool ShouldSerializeValue(object component) => true; } } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/CustomMenuItemCollection.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/CustomMenuItemCollection.cs new file mode 100644 index 00000000000..97217060bf2 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/CustomMenuItemCollection.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; + +namespace System.Windows.Forms.Design +{ + /// + /// A strongly-typed collection that stores ToolStripMenuItem objects for DesignerContextMenu + /// + internal class CustomMenuItemCollection : CollectionBase + { + /// + /// Constructor + /// + public CustomMenuItemCollection() + { + } + + /// + /// Add value to the collection + /// + public int Add(ToolStripItem value) + { + return List.Add(value); + } + + /// + /// Add range of values to the collection + /// + public void AddRange(ToolStripItem[] value) + { + for (int i = 0; (i < value.Length); i = (i + 1)) + { + Add(value[i]); + } + } + + /// + /// Abstract base class version for refreshing the items + /// + public virtual void RefreshItems() + { + + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionKeyboardBehavior.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionKeyboardBehavior.cs new file mode 100644 index 00000000000..9ba0a827644 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionKeyboardBehavior.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + internal sealed class DesignerActionKeyboardBehavior : Behavior.Behavior + { + private DesignerActionPanel _panel; + private readonly IMenuCommandService _menuService; + private readonly DesignerActionUIService _daUISvc; + private static readonly Guid s_vSStandardCommandSet97 = new Guid("{5efc7975-14bc-11cf-9b2b-00aa00573819}"); + + public DesignerActionKeyboardBehavior(DesignerActionPanel panel, IServiceProvider serviceProvider, BehaviorService behaviorService) : base(true, behaviorService) + { + _panel = panel; + if (serviceProvider != null) + { + _menuService = serviceProvider.GetService(typeof(IMenuCommandService)) as IMenuCommandService; + Debug.Assert(_menuService != null, "we should have found a menu service here..."); + _daUISvc = serviceProvider.GetService(typeof(DesignerActionUIService)) as DesignerActionUIService; + } + } + // THIS shoudl not stay here, creation of a custom command or of the real thing should be handled in the + // designeractionpanel itself + public override MenuCommand FindCommand(CommandID commandId) + { + if (_panel != null && _menuService != null) + { + // if the command we're looking for is handled by the panel, just tell VS that this command is disabled. otherwise let it through as usual... + foreach (CommandID candidateCommandId in _panel.FilteredCommandIDs) + { + // VisualStudio shell implements a mutable derived class from the base CommandID. The mutable class compares overridden properties instead of the read-only backing fields when testing equality of command IDs. Thus Equals method is asymmetrical when comparing VS implementation against the .Net implementation and the derived class's override that compares properties is the accurate one. + if (commandId.Equals(candidateCommandId)) + { + MenuCommand dummyMC = new MenuCommand(delegate { }, commandId); + dummyMC.Enabled = false; + return dummyMC; + } + } + // in case of a ctrl-tab we need to close the DAP + if (_daUISvc != null && commandId.Guid == DesignerActionKeyboardBehavior.s_vSStandardCommandSet97 && commandId.ID == 1124) + { + _daUISvc.HideUI(null); + } + } + return base.FindCommand(commandId); // this will route the request to the parent behavior + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionService.cs new file mode 100644 index 00000000000..43fb77cd4ed --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionService.cs @@ -0,0 +1,453 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; + +namespace System.Windows.Forms.Design +{ + /// + /// The DesignerActionService manages DesignerActions. All DesignerActions are associated with an object. DesignerActions can be added or removed at any given time. The DesignerActionService controls the expiration of DesignerActions by monitoring three basic events: selection change, component change, and timer expiration. Designer implementing this service will need to monitor the DesignerActionsChanged event on this class. This event will fire every time a change is made to any object's DesignerActions. + /// + public class DesignerActionService : IDisposable + { + private readonly Hashtable _designerActionLists; // this is how we store 'em. Syntax: key = object, value = DesignerActionListCollection + private DesignerActionListsChangedEventHandler _designerActionListsChanged; + private readonly IServiceProvider _serviceProvider; // standard service provider + private readonly ISelectionService _selSvc; // selection service + private readonly Hashtable _componentToVerbsEventHookedUp; //table component true/false + // Gaurd against ReEntrant Code. The Infragistics TabControlDesigner, Sets the Commands Status when the Verbs property is accesssed. This property is used in the OnVerbStatusChanged code here and hence causes recursion leading to Stack Overflow Exception. + private bool _reEntrantCode = false; + + /// + /// Standard constructor. A Service Provider is necessary for monitoring selection and component changes. + /// + public DesignerActionService(IServiceProvider serviceProvider) + { + if (serviceProvider != null) + { + _serviceProvider = serviceProvider; + IDesignerHost host = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost)); + host.AddService(typeof(DesignerActionService), this); + IComponentChangeService cs = (IComponentChangeService)serviceProvider.GetService(typeof(IComponentChangeService)); + if (cs != null) + { + cs.ComponentRemoved += new ComponentEventHandler(OnComponentRemoved); + } + _selSvc = (ISelectionService)serviceProvider.GetService(typeof(ISelectionService)); + if (_selSvc == null) + { + Debug.Fail("Either BehaviorService or ISelectionService is null, cannot continue."); + } + } + _designerActionLists = new Hashtable(); + _componentToVerbsEventHookedUp = new Hashtable(); + } + + /// + /// This event is thrown whenever a DesignerActionList is removed or added for any object. + /// + public event DesignerActionListsChangedEventHandler DesignerActionListsChanged + { + add => _designerActionListsChanged += value; + remove => _designerActionListsChanged -= value; + } + + /// + /// Adds a new collection of DesignerActions to be monitored with the related comp object. + /// + public void Add(IComponent comp, DesignerActionListCollection designerActionListCollection) + { + if (comp == null) + { + throw new ArgumentNullException("comp"); + } + if (designerActionListCollection == null) + { + throw new ArgumentNullException("designerActionListCollection"); + } + + DesignerActionListCollection dhlc = (DesignerActionListCollection)_designerActionLists[comp]; + if (dhlc != null) + { + dhlc.AddRange(designerActionListCollection); + } + else + { + _designerActionLists.Add(comp, designerActionListCollection); + } + + //fire event + OnDesignerActionListsChanged(new DesignerActionListsChangedEventArgs(comp, DesignerActionListsChangedType.ActionListsAdded, GetComponentActions(comp))); + } + + /// + /// Adds a new DesignerActionList to be monitored + /// with the related comp object + public void Add(IComponent comp, DesignerActionList actionList) + { + Add(comp, new DesignerActionListCollection( new[] { actionList } )); + } + + /// + /// Clears all objects and DesignerActions from the DesignerActionService. + /// + public void Clear() + { + if (_designerActionLists.Count == 0) + { + return; + } + + //this will represent the list of componets we just cleared + ArrayList compsRemoved = new ArrayList(_designerActionLists.Count); + foreach (DictionaryEntry entry in _designerActionLists) + { + compsRemoved.Add(entry.Key); + } + + //actually clear our hashtable + _designerActionLists.Clear(); + //fire our DesignerActionsChanged event for each comp we just removed + foreach (Component comp in compsRemoved) + { + OnDesignerActionListsChanged(new DesignerActionListsChangedEventArgs(comp, DesignerActionListsChangedType.ActionListsRemoved, GetComponentActions(comp))); + } + + } + + /// + /// Returns true if the DesignerActionService is currently managing the comp object. + /// + public bool Contains(IComponent comp) + { + if (comp == null) + { + throw new ArgumentNullException("comp"); + } + return _designerActionLists.Contains(comp); + } + + /// + /// Disposes all resources and unhooks all events. + /// + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing && _serviceProvider != null) + { + IDesignerHost host = (IDesignerHost)_serviceProvider.GetService(typeof(IDesignerHost)); + if (host != null) + { + host.RemoveService(typeof(DesignerActionService)); + } + + IComponentChangeService cs = (IComponentChangeService)_serviceProvider.GetService(typeof(IComponentChangeService)); + if (cs != null) + { + cs.ComponentRemoved -= new ComponentEventHandler(OnComponentRemoved); + } + } + } + + public DesignerActionListCollection GetComponentActions(IComponent component) + { + return GetComponentActions(component, ComponentActionsType.All); + } + + public virtual DesignerActionListCollection GetComponentActions(IComponent component, ComponentActionsType type) + { + if (component == null) + { + throw new ArgumentNullException("component"); + } + DesignerActionListCollection result = new DesignerActionListCollection(); + switch (type) + { + case ComponentActionsType.All: + GetComponentDesignerActions(component, result); + GetComponentServiceActions(component, result); + break; + case ComponentActionsType.Component: + GetComponentDesignerActions(component, result); + break; + case ComponentActionsType.Service: + GetComponentServiceActions(component, result); + break; + } + return result; + } + + protected virtual void GetComponentDesignerActions(IComponent component, DesignerActionListCollection actionLists) + { + if (component == null) + { + throw new ArgumentNullException("component"); + } + + if (actionLists == null) + { + throw new ArgumentNullException("actionLists"); + } + + if (component.Site is IServiceContainer sc) + { + DesignerCommandSet dcs = (DesignerCommandSet)sc.GetService(typeof(DesignerCommandSet)); + if (dcs != null) + { + DesignerActionListCollection pullCollection = dcs.ActionLists; + if (pullCollection != null) + { + actionLists.AddRange(pullCollection); + } + + // if we don't find any, add the verbs for this component there... + if (actionLists.Count == 0) + { + DesignerVerbCollection verbs = dcs.Verbs; + if (verbs != null && verbs.Count != 0) + { + ArrayList verbsArray = new ArrayList(); + bool hookupEvents = _componentToVerbsEventHookedUp[component] == null; + if (hookupEvents) + { + _componentToVerbsEventHookedUp[component] = true; + } + foreach (DesignerVerb verb in verbs) + { + if (hookupEvents) + { + verb.CommandChanged += new EventHandler(OnVerbStatusChanged); + } + if (verb.Enabled && verb.Visible) + { + verbsArray.Add(verb); + } + } + if (verbsArray.Count != 0) + { + DesignerActionVerbList davl = new DesignerActionVerbList((DesignerVerb[])verbsArray.ToArray(typeof(DesignerVerb))); + actionLists.Add(davl); + } + } + } + + // remove all the ones that are empty... ie GetSortedActionList returns nothing. we might waste some time doing this twice but don't have much of a choice here... the panel is not yet displayed and we want to know if a non empty panel is present... + // NOTE: We do this AFTER the verb check that way to disable auto verb upgrading you can just return an empty actionlist collection + if (pullCollection != null) + { + foreach (DesignerActionList actionList in pullCollection) + { + DesignerActionItemCollection collection = actionList.GetSortedActionItems(); + if (collection == null || collection.Count == 0) + { + actionLists.Remove(actionList); + } + } + } + } + } + } + + private void OnVerbStatusChanged(object sender, EventArgs args) + { + if (!_reEntrantCode) + { + try + { + _reEntrantCode = true; + if (_selSvc.PrimarySelection is IComponent comp) + { + if (comp.Site is IServiceContainer sc) + { + DesignerCommandSet dcs = (DesignerCommandSet)sc.GetService(typeof(DesignerCommandSet)); + foreach (DesignerVerb verb in dcs.Verbs) + { + if (verb == sender) + { + DesignerActionUIService dapUISvc = (DesignerActionUIService)sc.GetService(typeof(DesignerActionUIService)); + if (dapUISvc != null) + { + dapUISvc.Refresh(comp); // we need to refresh, a verb on the current panel has changed its state + } + } + } + } + } + } + finally + { + _reEntrantCode = false; + } + } + } + + protected virtual void GetComponentServiceActions(IComponent component, DesignerActionListCollection actionLists) + { + if (component == null) + { + throw new ArgumentNullException("component"); + } + + if (actionLists == null) + { + throw new ArgumentNullException("actionLists"); + } + + DesignerActionListCollection pushCollection = (DesignerActionListCollection)_designerActionLists[component]; + if (pushCollection != null) + { + actionLists.AddRange(pushCollection); + // remove all the ones that are empty... ie GetSortedActionList returns nothing. we might waste some time doing this twice but don't have much of a choice here... the panel is not yet displayed and we want to know if a non empty panel is present... + foreach (DesignerActionList actionList in pushCollection) + { + DesignerActionItemCollection collection = actionList.GetSortedActionItems(); + if (collection == null || collection.Count == 0) + { + actionLists.Remove(actionList); + } + } + } + } + + /// + /// We hook the OnComponentRemoved event so we can clean up all associated actions. + /// + private void OnComponentRemoved(object source, ComponentEventArgs ce) + { + Remove(ce.Component); + } + + /// + /// This fires our DesignerActionsChanged event. + /// + private void OnDesignerActionListsChanged(DesignerActionListsChangedEventArgs e) + { + _designerActionListsChanged?.Invoke(this, e); + } + + /// + /// This will remove all DesignerActions associated with the 'comp' object. All alarms will be unhooked and the DesignerActionsChagned event will be fired. + /// + public void Remove(IComponent comp) + { + if (comp == null) + { + throw new ArgumentNullException("comp"); + } + + if (!_designerActionLists.Contains(comp)) + { + return; + } + + _designerActionLists.Remove(comp); + OnDesignerActionListsChanged(new DesignerActionListsChangedEventArgs(comp, DesignerActionListsChangedType.ActionListsRemoved, GetComponentActions(comp))); + } + + /// + /// This will remove the specified Designeraction from the DesignerActionService. All alarms will be unhooked and the DesignerActionsChagned event will be fired. + /// + public void Remove(DesignerActionList actionList) + { + if (actionList == null) + { + throw new ArgumentNullException("actionList"); + } + + //find the associated component + foreach (IComponent comp in _designerActionLists.Keys) + { + if (((DesignerActionListCollection)_designerActionLists[comp]).Contains(actionList)) + { + Remove(comp, actionList); + break; + } + } + } + + /// + /// This will remove the all instances of the DesignerAction from the 'comp' object. If an alarm was set, it will be unhooked. This will also fire the DesignerActionChanged event. + /// + public void Remove(IComponent comp, DesignerActionList actionList) + { + if (comp == null) + { + throw new ArgumentNullException("comp"); + } + if (actionList == null) + { + throw new ArgumentNullException("actionList"); + } + if (!_designerActionLists.Contains(comp)) + { + return; + } + + DesignerActionListCollection actionLists = (DesignerActionListCollection)_designerActionLists[comp]; + if (!actionLists.Contains(actionList)) + { + return; + } + + if (actionLists.Count == 1) + { + //this is the last action for this object, remove the entire thing + Remove(comp); + } + else + { + //remove each instance of this action + ArrayList actionListsToRemove = new ArrayList(1); + foreach (DesignerActionList t in actionLists) + { + if (actionList.Equals(t)) + { + //found one to remove + actionListsToRemove.Add(t); + } + } + + foreach (DesignerActionList t in actionListsToRemove) + { + actionLists.Remove(t); + } + OnDesignerActionListsChanged(new DesignerActionListsChangedEventArgs(comp, DesignerActionListsChangedType.ActionListsRemoved, GetComponentActions(comp))); + } + } + + internal event DesignerActionUIStateChangeEventHandler DesignerActionUIStateChange + { + add + { + DesignerActionUIService dapUISvc = (DesignerActionUIService)_serviceProvider.GetService(typeof(DesignerActionUIService)); + if (dapUISvc != null) + { + dapUISvc.DesignerActionUIStateChange += value; + } + } + remove + { + DesignerActionUIService dapUISvc = (DesignerActionUIService)_serviceProvider.GetService(typeof(DesignerActionUIService)); + if (dapUISvc != null) + { + dapUISvc.DesignerActionUIStateChange -= value; + } + } + } + } + + public enum ComponentActionsType + { + All, + Component, + Service + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUI.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUI.cs new file mode 100644 index 00000000000..b195586e9e2 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUI.cs @@ -0,0 +1,1092 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// The DesignerActionUI is the designer/UI-specific implementation of the DesignerActions feature. This class instantiates the DesignerActionService and hooks to its DesignerActionsChanged event. Responding to this single event will enable the DesignerActionUI to perform all neceessary UI-related operations. Note that the DesignerActionUI uses the BehaviorService to manage all UI interaction. For every component containing a DesignerAction (determined by the DesignerActionsChagned event) there will be an associated DesignerActionGlyph and DesignerActionBehavior. Finally, the DesignerActionUI is also responsible for showing and managing the Action's context menus. Note that every DesignerAction context menu has an item that will bring up the DesignerActions option pane in the options dialog. + /// + internal class DesignerActionUI : IDisposable + { + private static readonly TraceSwitch s_designeActionPanelTraceSwitch = new TraceSwitch("DesigneActionPanelTrace", "DesignerActionPanel tracing"); + + private Adorner _designerActionAdorner; //used to add designeraction-related glyphs + private IServiceProvider _serviceProvider; //standard service provider + private ISelectionService _selSvc; //used to determine if comps have selection or not + private DesignerActionService _designerActionService; //this is how all designeractions will be managed + private DesignerActionUIService _designerActionUIService; //this is how all designeractions UI elements will be managed + private BehaviorService _behaviorService; //this is how all of our UI is implemented (glyphs, behaviors, etc...) + private readonly IMenuCommandService _menuCommandService; + private DesignerActionKeyboardBehavior _dapkb; //out keyboard behavior + private readonly Hashtable _componentToGlyph; //used for quick reference between compoments and our glyphs + private Control _marshalingControl; //used to invoke events on our main gui thread + private IComponent _lastPanelComponent; + + private readonly IUIService _uiService; + private readonly IWin32Window _mainParentWindow; + internal DesignerActionToolStripDropDown designerActionHost; + + private readonly MenuCommand _cmdShowDesignerActions; //used to respond to the Alt+Shft+F10 command + private bool _inTransaction = false; + private IComponent _relatedComponentTransaction; + private DesignerActionGlyph _relatedGlyphTransaction; + private readonly bool _disposeActionService; + private readonly bool _disposeActionUIService; + + private delegate void ActionChangedEventHandler(object sender, DesignerActionListsChangedEventArgs e); +#if DEBUG + internal static readonly TraceSwitch DropDownVisibilityDebug = new TraceSwitch("DropDownVisibilityDebug", "Debug ToolStrip Selection code"); +#else + internal static readonly TraceSwitch DropDownVisibilityDebug; +#endif + /// + /// Constructor that takes a service provider. This is needed to establish references to the BehaviorService and SelecteionService, as well as spin-up the DesignerActionService. + /// + public DesignerActionUI(IServiceProvider serviceProvider, Adorner containerAdorner) + { + _serviceProvider = serviceProvider; + _designerActionAdorner = containerAdorner; + _behaviorService = (BehaviorService)serviceProvider.GetService(typeof(BehaviorService)); + _menuCommandService = (IMenuCommandService)serviceProvider.GetService(typeof(IMenuCommandService)); + _selSvc = (ISelectionService)serviceProvider.GetService(typeof(ISelectionService)); + if (_behaviorService == null || _selSvc == null) + { + Debug.Fail("Either BehaviorService or ISelectionService is null, cannot continue."); + return; + } + + //query for our DesignerActionService + _designerActionService = (DesignerActionService)serviceProvider.GetService(typeof(DesignerActionService)); + if (_designerActionService == null) + { + //start the service + _designerActionService = new DesignerActionService(serviceProvider); + _disposeActionService = true; + } + _designerActionUIService = (DesignerActionUIService)serviceProvider.GetService(typeof(DesignerActionUIService)); + if (_designerActionUIService == null) + { + _designerActionUIService = new DesignerActionUIService(serviceProvider); + _disposeActionUIService = true; + } + _designerActionUIService.DesignerActionUIStateChange += new DesignerActionUIStateChangeEventHandler(OnDesignerActionUIStateChange); + _designerActionService.DesignerActionListsChanged += new DesignerActionListsChangedEventHandler(OnDesignerActionsChanged); + _lastPanelComponent = null; + + IComponentChangeService cs = (IComponentChangeService)serviceProvider.GetService(typeof(IComponentChangeService)); + if (cs != null) + { + cs.ComponentChanged += new ComponentChangedEventHandler(OnComponentChanged); + } + + if (_menuCommandService != null) + { + _cmdShowDesignerActions = new MenuCommand(new EventHandler(OnKeyShowDesignerActions), MenuCommands.KeyInvokeSmartTag); + _menuCommandService.AddCommand(_cmdShowDesignerActions); + } + + _uiService = (IUIService)serviceProvider.GetService(typeof(IUIService)); + if (_uiService != null) + _mainParentWindow = _uiService.GetDialogOwnerWindow(); + _componentToGlyph = new Hashtable(); + _marshalingControl = new Control(); + _marshalingControl.CreateControl(); + } + + /// + /// Disposes all UI-related objects and unhooks services. + /// + // Don't need to dispose of designerActionUIService. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] + public void Dispose() + { + if (_marshalingControl != null) + { + _marshalingControl.Dispose(); + _marshalingControl = null; + } + if (_serviceProvider != null) + { + IComponentChangeService cs = (IComponentChangeService)_serviceProvider.GetService(typeof(IComponentChangeService)); + if (cs != null) + { + cs.ComponentChanged -= new ComponentChangedEventHandler(OnComponentChanged); + } + + if (_cmdShowDesignerActions != null) + { + IMenuCommandService mcs = (IMenuCommandService)_serviceProvider.GetService(typeof(IMenuCommandService)); + if (mcs != null) + { + mcs.RemoveCommand(_cmdShowDesignerActions); + } + } + } + + _serviceProvider = null; + _behaviorService = null; + _selSvc = null; + if (_designerActionService != null) + { + _designerActionService.DesignerActionListsChanged -= new DesignerActionListsChangedEventHandler(OnDesignerActionsChanged); + if (_disposeActionService) + { + _designerActionService.Dispose(); + } + } + _designerActionService = null; + + if (_designerActionUIService != null) + { + _designerActionUIService.DesignerActionUIStateChange -= new DesignerActionUIStateChangeEventHandler(OnDesignerActionUIStateChange); + if (_disposeActionUIService) + { + _designerActionUIService.Dispose(); + } + } + _designerActionUIService = null; + _designerActionAdorner = null; + } + + public DesignerActionGlyph GetDesignerActionGlyph(IComponent comp) + { + return GetDesignerActionGlyph(comp, null); + } + + internal DesignerActionGlyph GetDesignerActionGlyph(IComponent comp, DesignerActionListCollection dalColl) + { + // check this component origin, this class or is it readyonly because inherited... + InheritanceAttribute attribute = (InheritanceAttribute)TypeDescriptor.GetAttributes(comp)[typeof(InheritanceAttribute)]; + if (attribute == InheritanceAttribute.InheritedReadOnly) + { // only do it if we can change the control... + return null; + } + + // we didnt get on, fetch it + if (dalColl == null) + { + dalColl = _designerActionService.GetComponentActions(comp); + } + + if (dalColl != null && dalColl.Count > 0) + { + DesignerActionGlyph dag = null; + if (_componentToGlyph[comp] == null) + { + DesignerActionBehavior dab = new DesignerActionBehavior(_serviceProvider, comp, dalColl, this); + + //if comp is a component then try to find a traycontrol associated with it... this should really be in ComponentTray but there is no behaviorService for the CT + if (!(comp is Control) || comp is ToolStripDropDown) + { + //Here, we'll try to get the traycontrol associated with the comp and supply the glyph with an alternative bounds + if (_serviceProvider.GetService(typeof(ComponentTray)) is ComponentTray compTray) + { + ComponentTray.TrayControl trayControl = compTray.GetTrayControlFromComponent(comp); + if (trayControl != null) + { + Rectangle trayBounds = trayControl.Bounds; + dag = new DesignerActionGlyph(dab, trayBounds, compTray); + } + } + } + + //either comp is a control or we failed to find a traycontrol (which could be the case for toolstripitem components) - in this case just create a standard glyoh. + if (dag == null) + { + //if the related comp is a control, then this shortcut will just hang off its bounds + dag = new DesignerActionGlyph(dab, _designerActionAdorner); + } + + if (dag != null) + { + //store off this relationship + _componentToGlyph.Add(comp, dag); + } + } + else + { + dag = _componentToGlyph[comp] as DesignerActionGlyph; + if (dag != null) + { + if (dag.Behavior is DesignerActionBehavior behavior) + { + behavior.ActionLists = dalColl; + } + dag.Invalidate(); // need to invalidate here too, someone could have called refresh too soon, causing the glyph to get created in the wrong place + } + } + return dag; + } + else + { + // the list is now empty... remove the panel and glyph for this control + RemoveActionGlyph(comp); + return null; + } + } + + /// + /// We monitor this event so we can update smart tag locations when controls move. + /// + private void OnComponentChanged(object source, ComponentChangedEventArgs ce) + { + //validate event args + if (ce.Component == null || ce.Member == null || !IsDesignerActionPanelVisible) + { + return; + } + + // If the smart tag is showing, we only move the smart tag if the changing component is the component for the currently showing smart tag. + if (_lastPanelComponent != null && !_lastPanelComponent.Equals(ce.Component)) + { + return; + } + + //if something changed on a component we have actions associated with then invalidate all (repaint & reposition) + if (_componentToGlyph[ce.Component] is DesignerActionGlyph glyph) + { + glyph.Invalidate(); + + if (ce.Member.Name.Equals("Dock")) + { // this is the only case were we don't require an explicit refresh + RecreatePanel(ce.Component as IComponent); // because 99% of the time the action is name "dock in parent container" and get replaced by "undock" + } + + if (ce.Member.Name.Equals("Location") || + ce.Member.Name.Equals("Width") || + ce.Member.Name.Equals("Height")) + { + // we don't need to regen, we just need to update location calculate the position of the form hosting the panel + UpdateDAPLocation(ce.Component as IComponent, glyph); + } + } + } + + private void RecreatePanel(IComponent comp) + { + if (_inTransaction || comp != _selSvc.PrimarySelection) + { // we only ever need to do that when the comp is the primary selection + return; + } + // we check wether or not we're in a transaction, if we are, we only the refresh at the end of the transaction to avoid flicker. + if (_serviceProvider.GetService(typeof(IDesignerHost)) is IDesignerHost host) + { + bool hostIsClosingTransaction = false; + if (host is IDesignerHostTransactionState hostTransactionState) + { + hostIsClosingTransaction = hostTransactionState.IsClosingTransaction; + } + if (host.InTransaction && !hostIsClosingTransaction) + { + host.TransactionClosed += new DesignerTransactionCloseEventHandler(DesignerTransactionClosed); + _inTransaction = true; + _relatedComponentTransaction = comp; + return; + } + } + RecreateInternal(comp); + } + + private void DesignerTransactionClosed(object sender, DesignerTransactionCloseEventArgs e) + { + if (e.LastTransaction && _relatedComponentTransaction != null) + { + // surprise surprise we can get multiple even with e.LastTransaction set to true, even though we unhook here this is because the list on which we enumerate (the event handler list) is copied before it's enumerated on which means that if the undo engine for example creates and commit a transaction during the OnCancel of another completed transaction we will get this twice. So we have to check also for relatedComponentTransaction != null + _inTransaction = false; + IDesignerHost host = _serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; + host.TransactionClosed -= new DesignerTransactionCloseEventHandler(DesignerTransactionClosed); + RecreateInternal(_relatedComponentTransaction); + _relatedComponentTransaction = null; + } + } + + private void RecreateInternal(IComponent comp) + { + //Debug.WriteLine("not in a transaction, do it now!"); + DesignerActionGlyph glyph = GetDesignerActionGlyph(comp); + if (glyph != null) + { + VerifyGlyphIsInAdorner(glyph); + // this could happen when a verb change state or suddendly a control gets a new action in the panel and we are the primary selection in that case there would not be a glyph active in the adorner to be shown because we update that on selection change. We have to do that here too. Sad really... + RecreatePanel(glyph); // recreate the DAP itself + UpdateDAPLocation(comp, glyph); // reposition the thing + } + } + private void RecreatePanel(Glyph glyphWithPanelToRegen) + { + // we don't want to do anything if the panel is not visible + if (!IsDesignerActionPanelVisible) + { + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.RecreatePanel] panel is not visible, bail"); + return; + } + //recreate a designeraction panel + if (glyphWithPanelToRegen != null) + { + if (glyphWithPanelToRegen.Behavior is DesignerActionBehavior behaviorWithPanelToRegen) + { + Debug.Assert(behaviorWithPanelToRegen.RelatedComponent != null, "could not find related component for this refresh"); + DesignerActionPanel dap = designerActionHost.CurrentPanel; // WE DO NOT RECREATE THE WHOLE THING / WE UPDATE THE TASKS - should flicker less + dap.UpdateTasks(behaviorWithPanelToRegen.ActionLists, new DesignerActionListCollection(), string.Format(SR.DesignerActionPanel_DefaultPanelTitle, + behaviorWithPanelToRegen.RelatedComponent.GetType().Name), null); + designerActionHost.UpdateContainerSize(); + } + } + } + + private void VerifyGlyphIsInAdorner(DesignerActionGlyph glyph) + { + if (glyph.IsInComponentTray) + { + ComponentTray compTray = _serviceProvider.GetService(typeof(ComponentTray)) as ComponentTray; + if (compTray.SelectionGlyphs != null && !compTray.SelectionGlyphs.Contains(glyph)) + { + compTray.SelectionGlyphs.Insert(0, glyph); + } + } + else + { + if (_designerActionAdorner != null && _designerActionAdorner.Glyphs != null && !_designerActionAdorner.Glyphs.Contains(glyph)) + { + _designerActionAdorner.Glyphs.Insert(0, glyph); + } + } + glyph.InvalidateOwnerLocation(); + } + + /// + /// This event is fired by the DesignerActionService in response to a DesignerActionCollection changing. The event args contains information about the related object, the type of change (added or removed) and the remaining DesignerActionCollection for the object. Note that when new DesignerActions are added, if the related control/ is not yet parented - we add these actions to a "delay" list and they are later created when the control is finally parented. + /// + private void OnDesignerActionsChanged(object sender, DesignerActionListsChangedEventArgs e) + { + // We need to invoke this async because the designer action service will raise this event from the thread pool. + if (_marshalingControl != null && _marshalingControl.IsHandleCreated) + { + _marshalingControl.BeginInvoke(new ActionChangedEventHandler(OnInvokedDesignerActionChanged), new object[] { sender, e }); + } + } + + private void OnDesignerActionUIStateChange(object sender, DesignerActionUIStateChangeEventArgs e) + { + IComponent comp = e.RelatedObject as IComponent; + Debug.Assert(comp != null || e.ChangeType == DesignerActionUIStateChangeType.Hide, "related object is not an IComponent, something is wrong here..."); + if (comp != null) + { + DesignerActionGlyph relatedGlyph = GetDesignerActionGlyph(comp); + if (relatedGlyph != null) + { + if (e.ChangeType == DesignerActionUIStateChangeType.Show) + { + if (relatedGlyph.Behavior is DesignerActionBehavior behavior) + { + behavior.ShowUI(relatedGlyph); + } + } + else if (e.ChangeType == DesignerActionUIStateChangeType.Hide) + { + if (relatedGlyph.Behavior is DesignerActionBehavior behavior) + { + behavior.HideUI(); + } + } + else if (e.ChangeType == DesignerActionUIStateChangeType.Refresh) + { + relatedGlyph.Invalidate(); + RecreatePanel((IComponent)e.RelatedObject); + } + } + } + else + { + if (e.ChangeType == DesignerActionUIStateChangeType.Hide) + { + HideDesignerActionPanel(); + } + } + } + + /// + /// This is the same as DesignerActionChanged, but it is invoked on our control's thread + /// + private void OnInvokedDesignerActionChanged(object sender, DesignerActionListsChangedEventArgs e) + { + IComponent relatedComponent = e.RelatedObject as IComponent; + DesignerActionGlyph g = null; + if (e.ChangeType == DesignerActionListsChangedType.ActionListsAdded) + { + if (relatedComponent == null) + { + Debug.Fail("How can we add a DesignerAction glyphs when it's related object is not an IComponent?"); + return; + } + IComponent primSel = _selSvc.PrimarySelection as IComponent; + if (primSel == e.RelatedObject) + { + g = GetDesignerActionGlyph(relatedComponent, e.ActionLists); + if (g != null) + { + VerifyGlyphIsInAdorner(g); + } + else + { + RemoveActionGlyph(e.RelatedObject); + } + } + } + if (e.ChangeType == DesignerActionListsChangedType.ActionListsRemoved && e.ActionLists.Count == 0) + { + //only remove our glyph if there are no more DesignerActions associated with it. + RemoveActionGlyph(e.RelatedObject); + } + else if (g != null) + { + // we need to recreate the panel here, since it's content has changed... + RecreatePanel(relatedComponent); + } + } + + /// + /// Called when our KeyShowDesignerActions menu command is fired (a.k.a. Alt+Shift+F10) - we will find the primary selection, see if it has designer actions, and if so - show the menu. + /// + private void OnKeyShowDesignerActions(object sender, EventArgs e) + { + ShowDesignerActionPanelForPrimarySelection(); + } + + // we cannot attach several menu command to the same command id, we need a single entry point, we put it in designershortcutui. but we need a way to call the show ui on the related behavior hence this internal function to hack it together. we return false if we have nothing to display, we hide it and return true if we're already displaying + internal bool ShowDesignerActionPanelForPrimarySelection() + { + //can't do anythign w/o selection service + if (_selSvc == null) + { + return false; + } + + object primarySelection = _selSvc.PrimarySelection; + //verfiy that we have obtained a valid component with designer actions + if (primarySelection == null || !_componentToGlyph.Contains(primarySelection)) + { + return false; + } + + DesignerActionGlyph glyph = (DesignerActionGlyph)_componentToGlyph[primarySelection]; + if (glyph != null && glyph.Behavior is DesignerActionBehavior) + { + // show the menu + if (glyph.Behavior is DesignerActionBehavior behavior) + { + if (!IsDesignerActionPanelVisible) + { + behavior.ShowUI(glyph); + return true; + } + else + { + behavior.HideUI(); + return false; + } + } + } + return false; + } + + /// + /// When all the DesignerActions have been removed for a particular object, we remove any UI (glyphs) that we may have been managing. + /// + internal void RemoveActionGlyph(object relatedObject) + { + if (relatedObject == null) + { + return; + } + if (IsDesignerActionPanelVisible && relatedObject == _lastPanelComponent) + { + HideDesignerActionPanel(); + } + + DesignerActionGlyph glyph = (DesignerActionGlyph)_componentToGlyph[relatedObject]; + if (glyph != null) + { + // Check ComponentTray first + if (_serviceProvider.GetService(typeof(ComponentTray)) is ComponentTray compTray && compTray.SelectionGlyphs != null) + { + if (compTray != null && compTray.SelectionGlyphs.Contains(glyph)) + { + compTray.SelectionGlyphs.Remove(glyph); + } + } + + if (_designerActionAdorner.Glyphs.Contains(glyph)) + { + _designerActionAdorner.Glyphs.Remove(glyph); + } + _componentToGlyph.Remove(relatedObject); + + // we only do this when we're in a transaction, see bug VSWHIDBEY 418709. This is for compat reason - infragistic. if we're not in a transaction, too bad, we don't update the screen + if (_serviceProvider.GetService(typeof(IDesignerHost)) is IDesignerHost host && host.InTransaction) + { + host.TransactionClosed += new DesignerTransactionCloseEventHandler(InvalidateGlyphOnLastTransaction); + _relatedGlyphTransaction = glyph; + } + } + } + + private void InvalidateGlyphOnLastTransaction(object sender, DesignerTransactionCloseEventArgs e) + { + if (e.LastTransaction) + { + IDesignerHost host = (_serviceProvider != null) ? _serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost : null; + if (host != null) + { + host.TransactionClosed -= new DesignerTransactionCloseEventHandler(InvalidateGlyphOnLastTransaction); + } + if (_relatedGlyphTransaction != null) + { + _relatedGlyphTransaction.InvalidateOwnerLocation(); + } + _relatedGlyphTransaction = null; + } + } + + internal void HideDesignerActionPanel() + { + if (IsDesignerActionPanelVisible) + { + designerActionHost.Close(); + } + } + + internal bool IsDesignerActionPanelVisible + { + get => (designerActionHost != null && designerActionHost.Visible); + } + + internal IComponent LastPanelComponent + { + get => (IsDesignerActionPanelVisible ? _lastPanelComponent : null); + } + + private void ToolStripDropDown_Closing(object sender, ToolStripDropDownClosingEventArgs e) + { + if (_cancelClose || e.Cancel) + { + e.Cancel = true; + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] cancelClose true, bail"); + return; + } + if (e.CloseReason == ToolStripDropDownCloseReason.ItemClicked) + { + e.Cancel = true; + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] ItemClicked: e.Cancel set to: " + e.Cancel.ToString()); + } + if (e.CloseReason == ToolStripDropDownCloseReason.Keyboard) + { + e.Cancel = false; + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] Keyboard: e.Cancel set to: " + e.Cancel.ToString()); + } + + if (e.Cancel == false) + { // we WILL disappear + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] Closing..."); + Debug.Assert(_lastPanelComponent != null, "last panel component should not be null here... " + + "(except if you're currently debugging VS where deactivation messages in the middle of the pump can mess up everything...)"); + if (_lastPanelComponent == null) + return; + // if we're actually closing get the coordinate of the last message, the one causing us to close, is it within the glyph coordinate. if it is that mean that someone just clicked back from the panel, on VS, but ON THE GLYPH, that means that he actually wants to close it. The activation change is going to do that for us but we should NOT reopen right away because he clicked on the glyph... this code is here to prevent this... + Point point = DesignerUtils.LastCursorPoint; + if (_componentToGlyph[_lastPanelComponent] is DesignerActionGlyph currentGlyph) + { + Point glyphCoord = GetGlyphLocationScreenCoord(_lastPanelComponent, currentGlyph); + if ((new Rectangle(glyphCoord, new Size(currentGlyph.Bounds.Width, currentGlyph.Bounds.Height))).Contains(point)) + { + DesignerActionBehavior behavior = currentGlyph.Behavior as DesignerActionBehavior; + behavior.IgnoreNextMouseUp = true; + } + currentGlyph.InvalidateOwnerLocation(); + } + _lastPanelComponent = null; + // panel is going away, pop the behavior that's on the stack... + Debug.Assert(_dapkb != null, "why is dapkb null?"); + System.Windows.Forms.Design.Behavior.Behavior popBehavior = _behaviorService.PopBehavior(_dapkb); + Debug.Assert(popBehavior is DesignerActionKeyboardBehavior, "behavior returned is of the wrong kind?"); + } + } + + internal Point UpdateDAPLocation(IComponent component, DesignerActionGlyph glyph) + { + if (component == null) + { // in case of a resize... + component = _lastPanelComponent; + } + + if (designerActionHost == null) + { + return Point.Empty; + } + + if (component == null || glyph == null) + { + return designerActionHost.Location; + } + + // check that the glyph is still visible in the adorner window + if (_behaviorService != null && + !_behaviorService.AdornerWindowControl.DisplayRectangle.IntersectsWith(glyph.Bounds)) + { + HideDesignerActionPanel(); + return designerActionHost.Location; + } + + Point glyphLocationScreenCoord = GetGlyphLocationScreenCoord(component, glyph); + Rectangle rectGlyph = new Rectangle(glyphLocationScreenCoord, glyph.Bounds.Size); + Point pt = DesignerActionPanel.ComputePreferredDesktopLocation(rectGlyph, designerActionHost.Size, out DockStyle edgeToDock); + glyph.DockEdge = edgeToDock; + designerActionHost.Location = pt; + return pt; + } + + private Point GetGlyphLocationScreenCoord(IComponent relatedComponent, Glyph glyph) + { + Point glyphLocationScreenCoord = new Point(0, 0); + if (relatedComponent is Control && !(relatedComponent is ToolStripDropDown)) + { + glyphLocationScreenCoord = _behaviorService.AdornerWindowPointToScreen(glyph.Bounds.Location); + } + //ISSUE: we can't have this special cased here - we should find a more generic approach to solving this problem + else if (relatedComponent is ToolStripItem) + { + if (relatedComponent is ToolStripItem item && item.Owner != null) + { + glyphLocationScreenCoord = _behaviorService.AdornerWindowPointToScreen(glyph.Bounds.Location); + } + } + else if (relatedComponent is IComponent) + { + if (_serviceProvider.GetService(typeof(ComponentTray)) is ComponentTray compTray) + { + glyphLocationScreenCoord = compTray.PointToScreen(glyph.Bounds.Location); + } + } + return glyphLocationScreenCoord; + } + + bool _cancelClose = false; + /// + /// This shows the actual chrome paenl that is created by the DesignerActionBehavior object. + /// + internal void ShowDesignerActionPanel(IComponent relatedComponent, DesignerActionPanel panel, DesignerActionGlyph glyph) + { + if (designerActionHost == null) + { + designerActionHost = new DesignerActionToolStripDropDown(this, _mainParentWindow) + { + AutoSize = false, + Padding = Padding.Empty, + Renderer = new NoBorderRenderer(), + Text = "DesignerActionTopLevelForm" + }; + designerActionHost.Closing += new ToolStripDropDownClosingEventHandler(ToolStripDropDown_Closing); + } + // set the accessible name of the panel to the same title as the panel header. do that every time + designerActionHost.AccessibleName = string.Format(SR.DesignerActionPanel_DefaultPanelTitle, relatedComponent.GetType().Name); + panel.AccessibleName = string.Format(SR.DesignerActionPanel_DefaultPanelTitle, relatedComponent.GetType().Name); + + + //GetDesignerActionGlyph(relatedComponent); // only here to update the ActionList collection on the behavior + designerActionHost.SetDesignerActionPanel(panel, glyph); + Point location = UpdateDAPLocation(relatedComponent, glyph); + + // check that the panel will have at least it's parent glyph visible on the adorner window + + if (_behaviorService != null && + _behaviorService.AdornerWindowControl.DisplayRectangle.IntersectsWith(glyph.Bounds)) + { + //behaviorService.AdornerWindowGraphics.IsVisible(glyph.Bounds)) { + if (_mainParentWindow != null && _mainParentWindow.Handle != IntPtr.Zero) + { + Debug.WriteLineIf(s_designeActionPanelTraceSwitch.TraceVerbose, "Assigning owner to mainParentWindow"); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "Assigning owner to mainParentWindow"); + UnsafeNativeMethods.SetWindowLong(new HandleRef(designerActionHost, designerActionHost.Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(_mainParentWindow, _mainParentWindow.Handle)); + } + + _cancelClose = true; + designerActionHost.Show(location); + designerActionHost.Focus(); + // when a control is drag and dropped and authoshow is set to true the vs designer is going to get activated as soon as the control is dropped we don't want to close the panel then, so we post a message (using the trick to call begin invoke) and once everything is settled re-activate the autoclose logic + designerActionHost.BeginInvoke(new EventHandler(OnShowComplete)); + // invalidate the glyph to have it point the other way + glyph.InvalidateOwnerLocation(); + _lastPanelComponent = relatedComponent; + // push new behavior for keyboard handling on the behavior stack + _dapkb = new DesignerActionKeyboardBehavior(designerActionHost.CurrentPanel, _serviceProvider, _behaviorService); + _behaviorService.PushBehavior(_dapkb); + } + } + + private void OnShowComplete(object sender, EventArgs e) + { + _cancelClose = false; + // force the panel to be the active window - for some reason someone else could have forced VS to become active for real while we were ignoring close. This might be bad cause we'd be in a bad state. + if (designerActionHost != null && designerActionHost.Handle != IntPtr.Zero && designerActionHost.Visible) + { + UnsafeNativeMethods.SetActiveWindow(new HandleRef(this, designerActionHost.Handle)); + designerActionHost.CheckFocusIsRight(); + } + } + } + + internal class DesignerActionToolStripDropDown : ToolStripDropDown + { + private readonly IWin32Window _mainParentWindow; + private ToolStripControlHost _panel; + private readonly DesignerActionUI _designerActionUI; + private bool _cancelClose = false; + private Glyph _relatedGlyph; + + public DesignerActionToolStripDropDown(DesignerActionUI designerActionUI, IWin32Window mainParentWindow) + { + _mainParentWindow = mainParentWindow; + _designerActionUI = designerActionUI; + } + + public DesignerActionPanel CurrentPanel + { + get + { + if (_panel != null) + { + return _panel.Control as DesignerActionPanel; + } + else + { + return null; + } + } + } + + // we're not topmost because we can show modal editors above us. + protected override bool TopMost + { + get => false; + } + + public void UpdateContainerSize() + { + if (CurrentPanel != null) + { + Size panelSize = CurrentPanel.GetPreferredSize(new Size(150, int.MaxValue)); + if (CurrentPanel.Size == panelSize) + { + // If the panel size didn't actually change, we still have to force a call to PerformLayout to make sure that controls get repositioned properly within the panel. The issue arises because we did a measure-only Layout that determined some sizes, and then we end up painting with those values even though there wasn't an actual Layout performed. + CurrentPanel.PerformLayout(); + } + else + { + CurrentPanel.Size = panelSize; + } + ClientSize = panelSize; + } + } + + public void CheckFocusIsRight() + { // hack to get the focus to NOT stay on ContainerControl + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "Checking focus..."); + IntPtr focusedControl = UnsafeNativeMethods.GetFocus(); + if (focusedControl == Handle) + { + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, " putting focus on the panel..."); + _panel.Focus(); + } + focusedControl = UnsafeNativeMethods.GetFocus(); + if (CurrentPanel != null && CurrentPanel.Handle == focusedControl) + { + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, " selecting next available control on the panel..."); + CurrentPanel.SelectNextControl(null, true, true, true, true); + } + UnsafeNativeMethods.GetFocus(); + } + + protected override void OnLayout(LayoutEventArgs levent) + { + base.OnLayout(levent); + + UpdateContainerSize(); + } + + protected override void OnClosing(ToolStripDropDownClosingEventArgs e) + { + + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "_____________________________Begin OnClose " + e.CloseReason.ToString()); + Debug.Indent(); + if (e.CloseReason == ToolStripDropDownCloseReason.AppFocusChange && _cancelClose) + { + _cancelClose = false; + e.Cancel = true; + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "cancel close prepopulated"); + } + // when we get closing event as a result of an activation change, pre-populate e.Cancel based on why we're exiting. + // - if it's a modal window that's owned by VS dont exit + // - if it's a window that's owned by the toolstrip dropdown dont exit + else if (e.CloseReason == ToolStripDropDownCloseReason.AppFocusChange || e.CloseReason == ToolStripDropDownCloseReason.AppClicked) + { + IntPtr hwndActivating = UnsafeNativeMethods.GetActiveWindow(); + if (Handle == hwndActivating && e.CloseReason == ToolStripDropDownCloseReason.AppClicked) + { + e.Cancel = false; + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] activation hasnt changed, but we've certainly clicked somewhere else."); + } + else if (WindowOwnsWindow(Handle, hwndActivating)) + { + // we're being de-activated for someone owned by the panel + e.Cancel = true; + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] Cancel close - the window activating is owned by this window"); + } + else if (_mainParentWindow != null && !WindowOwnsWindow(_mainParentWindow.Handle, hwndActivating)) + { + if (IsWindowEnabled(_mainParentWindow.Handle)) + { + // the activated windows is not a child/owned windows of the main top level windows let toolstripdropdown handle this + e.Cancel = false; + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] Call close: the activated windows is not a child/owned windows of the main top level windows "); + } + else + { + e.Cancel = true; + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] we're being deactivated by a foreign window, but the main window is not enabled - we should stay up"); + } + base.OnClosing(e); + Debug.Unindent(); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "_____________________________End OnClose e.Cancel: " + e.Cancel.ToString()); + return; + } + else + { + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] since the designer action panel dropdown doesnt own the activating window " + hwndActivating.ToString("x") + ", calling close. "); + } + + // what's the owner of the windows being activated? + IntPtr parent = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, hwndActivating), NativeMethods.GWL_HWNDPARENT); + // is it currently disabled (ie, the activating windows is in modal mode) + if (!IsWindowEnabled(parent)) + { + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] modal window activated - cancelling close"); + // we are in a modal case + e.Cancel = true; + } + } + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] calling base.OnClosing with e.Cancel: " + e.Cancel.ToString()); + base.OnClosing(e); + Debug.Unindent(); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "_____________________________End OnClose e.Cancel: " + e.Cancel.ToString()); + } + + public void SetDesignerActionPanel(DesignerActionPanel panel, Glyph relatedGlyph) + { + if (_panel != null && panel == (DesignerActionPanel)_panel.Control) + return; + Debug.Assert(relatedGlyph != null, "related glyph cannot be null"); + _relatedGlyph = relatedGlyph; + panel.SizeChanged += new EventHandler(PanelResized); + // hook up the event + if (_panel != null) + { + Items.Remove(_panel); + _panel.Dispose(); + _panel = null; + } + _panel = new ToolStripControlHost(panel) + { + // we don't want no margin + Margin = Padding.Empty, + Size = panel.Size + }; + + SuspendLayout(); + Size = panel.Size; + Items.Add(_panel); + ResumeLayout(); + if (Visible) + { + CheckFocusIsRight(); + } + + } + + private void PanelResized(object sender, System.EventArgs e) + { + Control ctrl = sender as Control; + if (Size.Width != ctrl.Size.Width || Size.Height != ctrl.Size.Height) + { + SuspendLayout(); + Size = ctrl.Size; + if (_panel != null) + { + _panel.Size = ctrl.Size; + } + _designerActionUI.UpdateDAPLocation(null, _relatedGlyph as DesignerActionGlyph); + ResumeLayout(); + } + } + + protected override void SetVisibleCore(bool visible) + { + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.SetVisibleCore] setting dropdown visible=" + visible.ToString()); + base.SetVisibleCore(visible); + if (visible) + { + CheckFocusIsRight(); + } + } + + /// + /// General purpose method, based on Control.Contains()... + /// Determines whether a given window (specified using native window handle) is a descendant of this control. This catches both contained descendants and 'owned' windows such as modal dialogs. Using window handles rather than Control objects allows it to catch un-managed windows as well. + /// + private static bool WindowOwnsWindow(IntPtr hWndOwner, IntPtr hWndDescendant) + { + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[WindowOwnsWindow] Testing if " + hWndOwner.ToString("x") + " is a owned by " + hWndDescendant.ToString("x") + "... "); +#if DEBUG + if (DesignerActionUI.DropDownVisibilityDebug.TraceVerbose) { + Debug.WriteLine("\t\tOWNER: " + GetControlInformation(hWndOwner)); + Debug.WriteLine("\t\tOWNEE: " + GetControlInformation(hWndDescendant)); + IntPtr claimedOwnerHwnd = UnsafeNativeMethods.GetWindowLong(new HandleRef(null, hWndDescendant), NativeMethods.GWL_HWNDPARENT); + Debug.WriteLine("OWNEE's CLAIMED OWNER: "+ GetControlInformation(claimedOwnerHwnd)); + } +#endif + if (hWndDescendant == hWndOwner) + { + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "they match, YES."); + return true; + } + + while (hWndDescendant != IntPtr.Zero) + { + hWndDescendant = UnsafeNativeMethods.GetWindowLong(new HandleRef(null, hWndDescendant), NativeMethods.GWL_HWNDPARENT); + if (hWndDescendant == IntPtr.Zero) + { + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "NOPE."); + return false; + } + if (hWndDescendant == hWndOwner) + { + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "YES."); + return true; + } + } + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "NO."); + return false; + } + // helper function for generating infomation about a particular control use AssertControlInformation if sticking in an assert - then the work to figure out the control info will only be done when the assertion is false. + internal static string GetControlInformation(IntPtr hwnd) + { + if (hwnd == IntPtr.Zero) + { + return "Handle is IntPtr.Zero"; + } +#if DEBUG + if (!DesignerActionUI.DropDownVisibilityDebug.TraceVerbose) { + return string.Empty; + } + int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(null, hwnd)); + StringBuilder sb = new StringBuilder(textLen+1); + UnsafeNativeMethods.GetWindowText(new HandleRef(null, hwnd), sb, sb.Capacity); + string typeOfControl = "Unknown"; + string nameOfControl = ""; + Control c = Control.FromHandle(hwnd); + if (c != null) { + typeOfControl = c.GetType().Name; + if (!string.IsNullOrEmpty(c.Name)) { + nameOfControl += c.Name; + } + else { + nameOfControl += "Unknown"; + // some extra debug info for toolstripdropdowns... + if (c is ToolStripDropDown dd) + { + if (dd.OwnerItem != null) + { + nameOfControl += "OwnerItem: [" + dd.OwnerItem.ToString() + "]"; + } + } + } + } + return sb.ToString() + "\r\n\t\t\tType: [" + typeOfControl + "] Name: [" + nameOfControl + "]"; +#else + return String.Empty; +#endif + + } + private bool IsWindowEnabled(IntPtr handle) + { + int style = (int)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_STYLE); + return (style & NativeMethods.WS_DISABLED) == 0; + } + + private void WmActivate(ref Message m) + { + if (unchecked((int)(long)m.WParam) == NativeMethods.WA_INACTIVE) + { + IntPtr hwndActivating = m.LParam; + if (WindowOwnsWindow(Handle, hwndActivating)) + { + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI WmActivate] setting cancel close true because WindowsOwnWindow"); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI WmActivate] checking the focus... " + GetControlInformation(UnsafeNativeMethods.GetFocus())); + _cancelClose = true; + } + else + { + _cancelClose = false; + } + } + else + { + _cancelClose = false; + } + base.WndProc(ref m); + } + + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_ACTIVATE: + WmActivate(ref m); + return; + } + base.WndProc(ref m); + } + + protected override bool ProcessDialogKey(Keys keyData) + { + // since we're not hosted in a form we need to do the same logic as Form.cs. If we get an enter key we need to find the current focused control. if it's a button, we click it and return that we handled the message + if (keyData == Keys.Enter) + { + IntPtr focusedControlPtr = UnsafeNativeMethods.GetFocus(); + Control focusedControl = Control.FromChildHandle(focusedControlPtr); + if (focusedControl is IButtonControl button && button is Control) + { + button.PerformClick(); + return true; + } + } + return base.ProcessDialogKey(keyData); + } + } + + internal class NoBorderRenderer : ToolStripProfessionalRenderer + { + protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) + { + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbItem.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbItem.cs new file mode 100644 index 00000000000..ba57b6eb8c6 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbItem.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel.Design; + +namespace System.Windows.Forms.Design +{ + internal class DesignerActionVerbItem : DesignerActionMethodItem + { + private readonly DesignerVerb _targetVerb; + + public DesignerActionVerbItem(DesignerVerb verb) + { + _targetVerb = verb ?? throw new ArgumentNullException(); + } + + public override string Category + { + get => "Verbs"; + } + + public override string Description + { + get => _targetVerb.Description; + } + + public override string DisplayName + { + get => _targetVerb.Text; + } + + public override string MemberName + { + get => null; + } + + public override bool IncludeAsDesignerVerb + { + get => false; + } + + public override void Invoke() => _targetVerb.Invoke(); + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbList.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbList.cs new file mode 100644 index 00000000000..6eef6dd68f8 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbList.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel.Design; + +namespace System.Windows.Forms.Design +{ + internal class DesignerActionVerbList : DesignerActionList + { + private DesignerVerb[] _verbs; + + public DesignerActionVerbList(DesignerVerb[] verbs) : base(null) + { + _verbs = verbs; + } + + public override bool AutoShow + { + get => false; + } + + public override DesignerActionItemCollection GetSortedActionItems() + { + DesignerActionItemCollection items = new DesignerActionItemCollection(); + for (int i = 0; i < _verbs.Length; i++) + { + if (_verbs[i].Visible && _verbs[i].Enabled && _verbs[i].Supported) + { + items.Add(new DesignerActionVerbItem(_verbs[i])); + } + } + return items; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerFrame.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerFrame.cs new file mode 100644 index 00000000000..040c5b254e7 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerFrame.cs @@ -0,0 +1,626 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms.Design.Behavior; +using Microsoft.Win32; + +namespace System.Windows.Forms.Design +{ + /// + /// This class implements our design time document. This is the outer window that encompases a designer. It maintains a control hierarchy that looks like this: + /// DesignerFrame + /// ScrollableControl + /// Designer + /// Splitter + /// ScrollableControl + /// Component Tray + /// The splitter and second scrollable control are created on demand when a tray is added. + /// + internal class DesignerFrame : Control, IOverlayService, ISplitWindowService, IContainsThemedScrollbarWindows + { + private readonly ISite _designerSite; + private readonly OverlayControl _designerRegion; + private Splitter _splitter; + private Control _designer; + private BehaviorService _behaviorService; + private readonly IUIService _uiService; + + /// + /// Initializes a new instance of the class. + /// + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + public DesignerFrame(ISite site) + { + Text = "DesignerFrame"; + _designerSite = site; + _designerRegion = new OverlayControl(site); + _uiService = _designerSite.GetService(typeof(IUIService)) as IUIService; + if (_uiService != null) + { + if (_uiService.Styles["ArtboardBackground"] is Color) + { + BackColor = (Color)_uiService.Styles["ArtboardBackground"]; + } + } + Controls.Add(_designerRegion); + // Now we must configure our designer to be at the correct location, and setup the autoscrolling for its container. + _designerRegion.AutoScroll = true; + _designerRegion.Dock = DockStyle.Fill; + } + + /// + /// Returns the scroll offset for the scrollable control that manages all overlays. This is needed by the BehaviorService so we can correctly invalidate our AdornerWindow based on scrollposition. + /// + internal Point AutoScrollPosition + { + get => _designerRegion.AutoScrollPosition; + } + + /// + /// Demand creates a ptr to the BehaviorService - we do this so we can + /// route keyboard message to it. + /// + private BehaviorService BehaviorService + { + get + { + if (_behaviorService == null) + { + _behaviorService = _designerSite.GetService(typeof(BehaviorService)) as BehaviorService; + } + return _behaviorService; + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_designer != null) + { + Control designerHolder = _designer; + _designer = null; + designerHolder.Visible = false; + designerHolder.Parent = null; + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + } + if (_splitter != null) + { + _splitter.SplitterMoved -= new SplitterEventHandler(OnSplitterMoved); + } + } + base.Dispose(disposing); + } + + private void ForceDesignerRedraw(bool focus) + { + if (_designer != null && _designer.IsHandleCreated) + { + NativeMethods.SendMessage(_designer.Handle, NativeMethods.WM_NCACTIVATE, focus ? 1 : 0, 0); + SafeNativeMethods.RedrawWindow(_designer.Handle, null, IntPtr.Zero, NativeMethods.RDW_FRAME); + } + } + + /// + /// Initializes this frame with the given designer view. + /// + public void Initialize(Control view) + { + _designer = view; + if (_designer is Form form) + { + form.TopLevel = false; + } + _designerRegion.Controls.Add(_designer); + SyncDesignerUI(); + _designer.Visible = true; + _designer.Enabled = true; + // We need to force handle creation here, since setting Visible = true won't if the control is already Visible = true. (UserControl starts out Visible true, Form does not) This guarantees that as controls are added to the root component their handles will be created correctly, and not the first time they're queried after load. + IntPtr handle = _designer.Handle; + // Hook the handler here, when we know that the designer object has already been set + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + } + + /// + /// When we get an lose focus, we need to make sure the form designer knows about it so it'll paint it's caption right. + /// + protected override void OnGotFocus(EventArgs e) + { + ForceDesignerRedraw(true); + ISelectionService selSvc = (ISelectionService)_designerSite.GetService(typeof(ISelectionService)); + if (selSvc != null) + { + if (selSvc.PrimarySelection is Control ctrl && !ctrl.IsDisposed) + { + UnsafeNativeMethods.NotifyWinEvent((int)AccessibleEvents.Focus, new HandleRef(ctrl, ctrl.Handle), NativeMethods.OBJID_CLIENT, 0); + } + } + } + + /// + /// When we get an lose focus, we need to make sure the form designer knows about it so it'll paint it's caption right. + /// + protected override void OnLostFocus(EventArgs e) + { + ForceDesignerRedraw(false); + } + + void OnSplitterMoved(object sender, SplitterEventArgs e) + { + // Dirty the designer. + if (_designerSite.GetService(typeof(IComponentChangeService)) is IComponentChangeService cs) + { + try + { + cs.OnComponentChanging(_designerSite.Component, null); + cs.OnComponentChanged(_designerSite.Component, null, null, null); + } + catch + { + } + } + } + + void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + if (e.Category == UserPreferenceCategory.Window && _designer != null) + { + SyncDesignerUI(); + } + } + + /// + /// We override this to do nothing. Otherwise, all the nice keyboard m messages we want would get run through the Form's keyboard handling procedure. + /// + protected override bool ProcessDialogKey(Keys keyData) + { + return false; + } + + void SyncDesignerUI() + { + Size selectionSize = DesignerUtils.GetAdornmentDimensions(AdornmentType.Maximum); + _designerRegion.AutoScrollMargin = selectionSize; + _designer.Location = new Point(selectionSize.Width, selectionSize.Height); + if (BehaviorService != null) + { + BehaviorService.SyncSelection(); + } + } + + /// + /// Base wndProc. All messages are sent to wndProc after getting filtered through the preProcessMessage function. Inheriting controls should call base.wndProc for any messages that they don't handle. + /// + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + // Provide MouseWheel access for scrolling + case NativeMethods.WM_MOUSEWHEEL: + // Send a message to ourselves to scroll + if (!_designerRegion._messageMouseWheelProcessed) + { + _designerRegion._messageMouseWheelProcessed = true; + NativeMethods.SendMessage(_designerRegion.Handle, NativeMethods.WM_MOUSEWHEEL, m.WParam, m.LParam); + return; + } + break; + // Provide keyboard access for scrolling + case NativeMethods.WM_KEYDOWN: + int wScrollNotify = 0; + int msg = 0; + int keycode = unchecked((int)(long)m.WParam) & 0xFFFF; + switch ((Keys)keycode) + { + case Keys.Up: + wScrollNotify = NativeMethods.SB_LINEUP; + msg = NativeMethods.WM_VSCROLL; + break; + case Keys.Down: + wScrollNotify = NativeMethods.SB_LINEDOWN; + msg = NativeMethods.WM_VSCROLL; + break; + case Keys.PageUp: + wScrollNotify = NativeMethods.SB_PAGEUP; + msg = NativeMethods.WM_VSCROLL; + break; + case Keys.PageDown: + wScrollNotify = NativeMethods.SB_PAGEDOWN; + msg = NativeMethods.WM_VSCROLL; + break; + case Keys.Home: + wScrollNotify = NativeMethods.SB_TOP; + msg = NativeMethods.WM_VSCROLL; + break; + case Keys.End: + wScrollNotify = NativeMethods.SB_BOTTOM; + msg = NativeMethods.WM_VSCROLL; + break; + case Keys.Left: + wScrollNotify = NativeMethods.SB_LINEUP; + msg = NativeMethods.WM_HSCROLL; + break; + case Keys.Right: + wScrollNotify = NativeMethods.SB_LINEDOWN; + msg = NativeMethods.WM_HSCROLL; + break; + } + if ((msg == NativeMethods.WM_VSCROLL) + || (msg == NativeMethods.WM_HSCROLL)) + { + // Send a message to ourselves to scroll + NativeMethods.SendMessage(_designerRegion.Handle, msg, NativeMethods.Util.MAKELONG(wScrollNotify, 0), 0); + return; + } + break; + case NativeMethods.WM_CONTEXTMENU: + NativeMethods.SendMessage(_designer.Handle, m.Msg, m.WParam, m.LParam); + return; + } + base.WndProc(ref m); + } + + /// + /// Pushes the given control on top of the overlay list. This is a "push" operation, meaning that it forces this control to the top of the existing overlay list. + /// + int IOverlayService.PushOverlay(Control control) => _designerRegion.PushOverlay(control); + + /// + /// Removes the given control from the overlay list. Unlike pushOverlay, this can remove a control from the middle of the overlay list. + /// + void IOverlayService.RemoveOverlay(Control control) + { + _designerRegion.RemoveOverlay(control); + } + + /// + /// Inserts the overlay. + /// + void IOverlayService.InsertOverlay(Control control, int index) + { + _designerRegion.InsertOverlay(control, index); + } + + /// + /// Invalidate child overlays + /// + void IOverlayService.InvalidateOverlays(Rectangle screenRectangle) + { + _designerRegion.InvalidateOverlays(screenRectangle); + } + + /// + /// Invalidate child overlays + /// + void IOverlayService.InvalidateOverlays(Region screenRegion) + { + _designerRegion.InvalidateOverlays(screenRegion); + } + + /// + /// Requests the service to add a window 'pane'. + /// + void ISplitWindowService.AddSplitWindow(Control window) + { + if (_splitter == null) + { + _splitter = new Splitter(); + if (_uiService != null && _uiService.Styles["HorizontalResizeGrip"] is Color) + { + _splitter.BackColor = (Color)_uiService.Styles["HorizontalResizeGrip"]; + } + else + { + _splitter.BackColor = SystemColors.Control; + } + _splitter.BorderStyle = BorderStyle.Fixed3D; + _splitter.Height = 7; + _splitter.Dock = DockStyle.Bottom; + _splitter.SplitterMoved += new SplitterEventHandler(OnSplitterMoved); + } + SuspendLayout(); + window.Dock = DockStyle.Bottom; + // Compute a minimum height for this window. + int minHeight = 80; + if (window.Height < minHeight) + { + window.Height = minHeight; + } + Controls.Add(_splitter); + Controls.Add(window); + ResumeLayout(); + } + + /// + /// Requests the service to remove a window 'pane'. + /// + void ISplitWindowService.RemoveSplitWindow(Control window) + { + SuspendLayout(); + Controls.Remove(window); + Controls.Remove(_splitter); + ResumeLayout(); + } + + /// + /// Returns IEnumerable of all windows which need to be themed when running inside VS We don't know how to do theming here but we know which windows need to be themed. The two ScrollableControls that hold the designer and the tray need to be themed, all of the children of the designed form should not be themed. The tray contains only conrols which are not visible in the user app but are visible inside VS. As a result, we want to theme all windows within the tray but only the top window for the designer pane. + /// + IEnumerable IContainsThemedScrollbarWindows.ThemedScrollbarWindows() + { + ArrayList windows = new ArrayList(); + foreach (Control c in Controls) + { + ThemedScrollbarWindow windowInfo = new ThemedScrollbarWindow { Handle = c.Handle }; + if (c is OverlayControl) + { + windowInfo.Mode = ThemedScrollbarMode.OnlyTopLevel; + } + else + { + windowInfo.Mode = ThemedScrollbarMode.All; + } + windows.Add(windowInfo); + } + return windows; + } + + /// + /// This is a scrollable control that supports additional floating overlay controls. + /// + private class OverlayControl : ScrollableControl + { + private readonly ArrayList _overlayList; + private readonly IServiceProvider _provider; + internal bool _messageMouseWheelProcessed; + private BehaviorService _behaviorService; + + /// + /// Creates a new overlay control. + /// + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + public OverlayControl(IServiceProvider provider) + { + _provider = provider; + _overlayList = new ArrayList(); + AutoScroll = true; + Text = "OverlayControl"; + } + + protected override AccessibleObject CreateAccessibilityInstance() + { + return new OverlayControlAccessibleObject(this); + } + + /// + /// Demand creates a ptr to the BehaviorService + /// + private BehaviorService BehaviorService + { + get + { + if (_behaviorService == null) + { + _behaviorService = _provider.GetService(typeof(BehaviorService)) as BehaviorService; + } + return _behaviorService; + } + } + + /// + /// At handle creation time we request the designer's handle and parent it. + /// + protected override void OnCreateControl() + { + base.OnCreateControl(); + // Loop through all of the overlays, create them, and hook them up + if (_overlayList != null) + { + foreach (Control c in _overlayList) + { + ParentOverlay(c); + } + } + + // We've reparented everything, which means that our selection UI is probably out of sync. Ask it to sync. + if (BehaviorService != null) + { + BehaviorService.SyncSelection(); + } + } + + /// + /// We override onLayout to provide our own custom layout functionality. This just overlaps all of the controls. + /// + protected override void OnLayout(LayoutEventArgs e) + { + base.OnLayout(e); + Rectangle client = DisplayRectangle; + + // Loop through all of the overlays and size them. Also make sure that they are still on top of the zorder, because a handle recreate could have changed this. + if (_overlayList != null) + { + foreach (Control c in _overlayList) + { + c.Bounds = client; + } + } + } + + /// + /// Called to parent an overlay window into our document. This assumes that we call in reverse stack order, as it always pushes to the top of the z-order. + /// + private void ParentOverlay(Control control) + { + NativeMethods.SetParent(control.Handle, Handle); + SafeNativeMethods.SetWindowPos(control.Handle, (IntPtr)NativeMethods.HWND_TOP, 0, 0, 0, 0, + NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE); + } + + /// + /// Pushes the given control on top of the overlay list. This is a "push" operation, meaning that it forces this control to the top of the existing overlay list. + /// + public int PushOverlay(Control control) + { + Debug.Assert(_overlayList.IndexOf(control) == -1, "Duplicate overlay in overlay service :" + control.GetType().FullName); + _overlayList.Add(control); + // We cheat a bit here. We need to have these components parented, but we don't want them to effect our layout. + if (IsHandleCreated) + { + ParentOverlay(control); + control.Bounds = DisplayRectangle; + } + return _overlayList.IndexOf(control); + } + + /// + /// Removes the given control from the overlay list. Unlike pushOverlay, this can remove a control from the middle of the overlay list. + /// + public void RemoveOverlay(Control control) + { + Debug.Assert(_overlayList.IndexOf(control) != -1, "Control is not in overlay service :" + control.GetType().FullName); + _overlayList.Remove(control); + control.Visible = false; + control.Parent = null; + } + + /// + /// Inserts Overlay. + /// + public void InsertOverlay(Control control, int index) + { + Debug.Assert(_overlayList.IndexOf(control) == -1, "Duplicate overlay in overlay service :" + control.GetType().FullName); + Control c = (Control)_overlayList[index]; + RemoveOverlay(c); + PushOverlay(control); + PushOverlay(c); + c.Visible = true; + } + + /// + /// Invalidates overlays that intersect with the given section of the screen; + /// + public void InvalidateOverlays(Rectangle screenRectangle) + { + // paint in inverse order so that things at the front paint last. + for (int i = _overlayList.Count - 1; i >= 0; i--) + { + if (_overlayList[i] is Control overlayControl) + { + Rectangle invalidateRect = new Rectangle(overlayControl.PointToClient(screenRectangle.Location), screenRectangle.Size); + if (overlayControl.ClientRectangle.IntersectsWith(invalidateRect)) + { + overlayControl.Invalidate(invalidateRect); + } + } + } + } + + /// + /// Invalidates overlays that intersect with the given section of the screen; + /// + public void InvalidateOverlays(Region screenRegion) + { + + + // paint in inverse order so that things at the front paint last. + for (int i = _overlayList.Count - 1; i >= 0; i--) + { + if (_overlayList[i] is Control overlayControl) + { + Rectangle overlayControlScreenBounds = overlayControl.Bounds; + overlayControlScreenBounds.Location = overlayControl.PointToScreen(overlayControl.Location); + + using (Region intersectionRegion = screenRegion.Clone()) + { + // get the intersection of everything on the screen that's invalidating and the overlaycontrol + intersectionRegion.Intersect(overlayControlScreenBounds); + + // translate this down to overlay control coordinates. + intersectionRegion.Translate(-overlayControlScreenBounds.X, -overlayControlScreenBounds.Y); + overlayControl.Invalidate(intersectionRegion); + } + + } + } + + } + /// + /// Need to know when child windows are created so we can properly set the Z-order + /// + protected override void WndProc(ref Message m) + { + base.WndProc(ref m); + + if (m.Msg == NativeMethods.WM_PARENTNOTIFY && NativeMethods.Util.LOWORD(unchecked((int)(long)m.WParam)) == (short)NativeMethods.WM_CREATE) + { + if (_overlayList != null) + { + bool ourWindow = false; + foreach (Control c in _overlayList) + { + if (c.IsHandleCreated && m.LParam == c.Handle) + { + ourWindow = true; + break; + } + } + + if (!ourWindow) + { + foreach (Control c in _overlayList) + { + SafeNativeMethods.SetWindowPos(c.Handle, (IntPtr)NativeMethods.HWND_TOP, 0, 0, 0, 0, + NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE); + } + } + } + } + else if ((m.Msg == NativeMethods.WM_VSCROLL || m.Msg == NativeMethods.WM_HSCROLL) && BehaviorService != null) + { + BehaviorService.SyncSelection(); + } + else if ((m.Msg == NativeMethods.WM_MOUSEWHEEL)) + { + _messageMouseWheelProcessed = false; + if (BehaviorService != null) + { + BehaviorService.SyncSelection(); + } + } + } + + public class OverlayControlAccessibleObject : Control.ControlAccessibleObject + { + public OverlayControlAccessibleObject(OverlayControl owner) : base(owner) + { + } + + public override AccessibleObject HitTest(int x, int y) + { + // Since the SelectionUIOverlay in first in the z-order, it normally gets + // returned from accHitTest. But we'd rather expose the form that is being + // designed. + // + foreach (Control c in Owner.Controls) + { + AccessibleObject cao = c.AccessibilityObject; + if (cao.Bounds.Contains(x, y)) + { + return cao; + } + } + + return base.HitTest(x, y); + } + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerToolStripControlHost.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerToolStripControlHost.cs new file mode 100644 index 00000000000..87a141bf197 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerToolStripControlHost.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// This internal class is used by the new ToolStripDesigner to add a dummy node to the end. This class inherits from WinBarControlHost and overrides the CanSelect property so that the dummy Node when shown in the designer doesnt show selection on Mouse movements. The image is set to theDummyNodeImage embedded into the resources. + /// + + internal class DesignerToolStripControlHost : ToolStripControlHost, IComponent + { + private BehaviorService _behaviorService; + // disable csharp compiler warning #0414: field assigned unused value +#pragma warning disable 0414 + internal ToolStrip _parent = null; +#pragma warning restore 0414 + + public DesignerToolStripControlHost(Control c) : base(c) + { + // this ToolStripItem should not have defaultPadding. + Margin = Padding.Empty; + } + + /// + /// We need to return Default size for Editor ToolStrip (92, 22). + /// + protected override Size DefaultSize + { + get => new Size(92, 22); + } + + internal GlyphCollection GetGlyphs(ToolStrip parent, GlyphCollection glyphs, System.Windows.Forms.Design.Behavior.Behavior standardBehavior) + { + if (_behaviorService == null) + { + _behaviorService = (BehaviorService)parent.Site.GetService(typeof(BehaviorService)); + } + Point loc = _behaviorService.ControlToAdornerWindow(Parent); + Rectangle r = Bounds; + r.Offset(loc); + r.Inflate(-2, -2); + glyphs.Add(new MiniLockedBorderGlyph(r, SelectionBorderGlyphType.Top, standardBehavior, true)); + glyphs.Add(new MiniLockedBorderGlyph(r, SelectionBorderGlyphType.Bottom, standardBehavior, true)); + glyphs.Add(new MiniLockedBorderGlyph(r, SelectionBorderGlyphType.Left, standardBehavior, true)); + glyphs.Add(new MiniLockedBorderGlyph(r, SelectionBorderGlyphType.Right, standardBehavior, true)); + return glyphs; + } + + internal void RefreshSelectionGlyph() + { + if (Control is ToolStrip miniToolStrip) + { + if (miniToolStrip.Renderer is ToolStripTemplateNode.MiniToolStripRenderer renderer) + { + renderer.State = (int)TemplateNodeSelectionState.None; + miniToolStrip.Invalidate(); + } + } + } + + internal void SelectControl() + { + if (Control is ToolStrip miniToolStrip) + { + if (miniToolStrip.Renderer is ToolStripTemplateNode.MiniToolStripRenderer renderer) + { + renderer.State = (int)TemplateNodeSelectionState.TemplateNodeSelected; + miniToolStrip.Focus(); + miniToolStrip.Invalidate(); + } + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerUtils.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerUtils.cs new file mode 100644 index 00000000000..2cde053640b --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerUtils.cs @@ -0,0 +1,963 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.ComponentModel.Design.Serialization; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// Contains designer utilities. + /// + internal static class DesignerUtils + { + private static Size s_minDragSize = Size.Empty; + //brush used to draw a 'hover' state over a designer action glyph + private static SolidBrush s_hoverBrush = new SolidBrush(Color.FromArgb(50, SystemColors.Highlight)); + //brush used to draw the resizeable selection borders around controls/components + private static HatchBrush s_selectionBorderBrush = new HatchBrush(HatchStyle.Percent50, SystemColors.ControlDarkDark, Color.Transparent); + //Pens and Brushes used via GDI to render our grabhandles + private static IntPtr s_grabHandleFillBrushPrimary = SafeNativeMethods.CreateSolidBrush(ColorTranslator.ToWin32(SystemColors.Window)); + private static IntPtr s_grabHandleFillBrush = SafeNativeMethods.CreateSolidBrush(ColorTranslator.ToWin32(SystemColors.ControlText)); + private static IntPtr s_grabHandlePenPrimary = SafeNativeMethods.CreatePen(NativeMethods.PS_SOLID, 1, ColorTranslator.ToWin32(SystemColors.ControlText)); + private static IntPtr s_grabHandlePen = SafeNativeMethods.CreatePen(NativeMethods.PS_SOLID, 1, ColorTranslator.ToWin32(SystemColors.Window)); + + //The box-like image used as the user is dragging comps from the toolbox + private static Bitmap s_boxImage = null; + public static int BOXIMAGESIZE = 16; + + // selection border size + public static int SELECTIONBORDERSIZE = 1; + // Although the selection border is only 1, we actually want a 3 pixel hittestarea + public static int SELECTIONBORDERHITAREA = 3; + + // We want to make sure that the 1 pixel selectionborder is centered on the handles. The fact that the border is actually 3 pixels wide works like magic. If you draw a picture, then you will see why. + //grabhandle size (diameter) + public static int HANDLESIZE = 7; + //how much should the grabhandle overlap the control + public static int HANDLEOVERLAP = 2; + //we want the selection border to be centered on a grabhandle, so how much do. we need to offset the border from the control to make that happen + public static int SELECTIONBORDEROFFSET = ((HANDLESIZE - SELECTIONBORDERSIZE) / 2) - HANDLEOVERLAP; + + //no-resize handle size (diameter) + public static int NORESIZEHANDLESIZE = 5; + //we want the selection border to be centered on a grabhandle, so how much do + //we need to offset the border from the control to make that happen + public static int NORESIZEBORDEROFFSET = ((NORESIZEHANDLESIZE - SELECTIONBORDERSIZE) / 2); + + //lock handle height + public static int LOCKHANDLEHEIGHT = 9; + //total lock handle width + public static int LOCKHANDLEWIDTH = 7; + //how much should the lockhandle overlap the control + public static int LOCKHANDLEOVERLAP = 2; + //we want the selection border to be centered on the no-resize handle, so calculate how many pixels we need + //to offset the selection border from the control -- since the handle is not square, we need one in each direction + public static int LOCKEDSELECTIONBORDEROFFSET_Y = ((LOCKHANDLEHEIGHT - SELECTIONBORDERSIZE) / 2) - LOCKHANDLEOVERLAP; + public static int LOCKEDSELECTIONBORDEROFFSET_X = ((LOCKHANDLEWIDTH - SELECTIONBORDERSIZE) / 2) - LOCKHANDLEOVERLAP; + + // upper rectangle size (diameter) + public static int LOCKHANDLESIZE_UPPER = 5; + // lower rectangle size + public static int LOCKHANDLEHEIGHT_LOWER = 6; + public static int LOCKHANDLEWIDTH_LOWER = 7; + + //Offset used when drawing the upper rect of a lock handle + public static int LOCKHANDLEUPPER_OFFSET = (LOCKHANDLEWIDTH_LOWER - LOCKHANDLESIZE_UPPER) / 2; + //Offset used when drawing the lower rect of a lock handle + public static int LOCKHANDLELOWER_OFFSET = (LOCKHANDLEHEIGHT - LOCKHANDLEHEIGHT_LOWER); + + public static int CONTAINERGRABHANDLESIZE = 15; + //delay for showing snaplines on keyboard movements + public static int SNAPELINEDELAY = 1000; + + //min new row/col style size for the table layout panel + public static int MINIMUMSTYLESIZE = 20; + public static int MINIMUMSTYLEPERCENT = 50; + + //min width/height used to create bitmap to paint control into. + public static int MINCONTROLBITMAPSIZE = 1; + //min size for row/col style during a resize drag operation + public static int MINUMUMSTYLESIZEDRAG = 8; + //min # of rows/cols for the tablelayoutpanel when it is newly created + public static int DEFAULTROWCOUNT = 2; + public static int DEFAULTCOLUMNCOUNT = 2; + + //size of the col/row grab handle glyphs for teh table layout panel + public static int RESIZEGLYPHSIZE = 4; + + //default value for Form padding if it has not been set in the designer (usability study request) + public static int DEFAULTFORMPADDING = 9; + + //use these value to signify ANY of the right, top, left, center, or bottom alignments with the ContentAlignment enum. + public static readonly ContentAlignment anyTopAlignment = ContentAlignment.TopLeft | ContentAlignment.TopCenter | ContentAlignment.TopRight; + /* UNUSED: private static readonly ContentAlignment anyBottom = ContentAlignment.BottomLeft | ContentAlignment.BottomCenter | ContentAlignment.BottomRight;*/ + public static readonly ContentAlignment anyMiddleAlignment = ContentAlignment.MiddleLeft | ContentAlignment.MiddleCenter | ContentAlignment.MiddleRight; + + /// + /// Scale all hardcoded sizes if needed + /// + static DesignerUtils() + { + if (!DpiHelper.IsScalingRequired) + { + return; + } + + BOXIMAGESIZE = DpiHelper.LogicalToDeviceUnitsX(BOXIMAGESIZE); + SELECTIONBORDERSIZE = DpiHelper.LogicalToDeviceUnitsX(SELECTIONBORDERSIZE); + SELECTIONBORDERHITAREA = DpiHelper.LogicalToDeviceUnitsX(SELECTIONBORDERHITAREA); + HANDLESIZE = DpiHelper.LogicalToDeviceUnitsX(HANDLESIZE); + HANDLEOVERLAP = DpiHelper.LogicalToDeviceUnitsX(HANDLEOVERLAP); + NORESIZEHANDLESIZE = DpiHelper.LogicalToDeviceUnitsX(NORESIZEHANDLESIZE); + LOCKHANDLEHEIGHT = DpiHelper.LogicalToDeviceUnitsY(LOCKHANDLEHEIGHT); + LOCKHANDLEWIDTH = DpiHelper.LogicalToDeviceUnitsX(LOCKHANDLEWIDTH); + LOCKHANDLEOVERLAP = DpiHelper.LogicalToDeviceUnitsX(LOCKHANDLEOVERLAP); + LOCKHANDLESIZE_UPPER = DpiHelper.LogicalToDeviceUnitsX(LOCKHANDLESIZE_UPPER); + LOCKHANDLEHEIGHT_LOWER = DpiHelper.LogicalToDeviceUnitsY(LOCKHANDLEHEIGHT_LOWER); + LOCKHANDLEWIDTH_LOWER = DpiHelper.LogicalToDeviceUnitsX(LOCKHANDLEWIDTH_LOWER); + CONTAINERGRABHANDLESIZE = DpiHelper.LogicalToDeviceUnitsX(CONTAINERGRABHANDLESIZE); + RESIZEGLYPHSIZE = DpiHelper.LogicalToDeviceUnitsX(RESIZEGLYPHSIZE); + + SELECTIONBORDEROFFSET = ((HANDLESIZE - SELECTIONBORDERSIZE) / 2) - HANDLEOVERLAP; + NORESIZEBORDEROFFSET = ((NORESIZEHANDLESIZE - SELECTIONBORDERSIZE) / 2); + LOCKEDSELECTIONBORDEROFFSET_Y = ((LOCKHANDLEHEIGHT - SELECTIONBORDERSIZE) / 2) - LOCKHANDLEOVERLAP; + LOCKEDSELECTIONBORDEROFFSET_X = ((LOCKHANDLEWIDTH - SELECTIONBORDERSIZE) / 2) - LOCKHANDLEOVERLAP; + LOCKHANDLEUPPER_OFFSET = (LOCKHANDLEWIDTH_LOWER - LOCKHANDLESIZE_UPPER) / 2; + LOCKHANDLELOWER_OFFSET = (LOCKHANDLEHEIGHT - LOCKHANDLEHEIGHT_LOWER); + } + + /// + /// Used when the user clicks and drags a toolbox item onto the documentdesigner - this is the small box that is painted beneath the mouse pointer. + /// + public static Image BoxImage + { + get + { + if (s_boxImage == null) + { + s_boxImage = new Bitmap(BOXIMAGESIZE, BOXIMAGESIZE, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); + using (Graphics g = Graphics.FromImage(s_boxImage)) + { + g.FillRectangle(new SolidBrush(SystemColors.InactiveBorder), 0, 0, BOXIMAGESIZE, BOXIMAGESIZE); + g.DrawRectangle(new Pen(SystemColors.ControlDarkDark), 0, 0, BOXIMAGESIZE - 1, BOXIMAGESIZE - 1); + } + } + return s_boxImage; + } + } + + /// + /// Used by Designer action glyphs to render a 'mouse hover' state. + /// + public static Brush HoverBrush + { + get => s_hoverBrush; + } + + /// + /// Demand created size used to determine how far the user needs to drag the mouse before a drag operation starts. + /// + public static Size MinDragSize + { + get + { + if (s_minDragSize == Size.Empty) + { + Size minDrag = SystemInformation.DragSize; + Size minDblClick = SystemInformation.DoubleClickSize; + s_minDragSize.Width = Math.Max(minDrag.Width, minDblClick.Width); + s_minDragSize.Height = Math.Max(minDrag.Height, minDblClick.Height); + } + return s_minDragSize; + } + } + + public static Point LastCursorPoint + { + get + { + int lastXY = SafeNativeMethods.GetMessagePos(); + return new Point(NativeMethods.Util.SignedLOWORD(lastXY), NativeMethods.Util.SignedHIWORD(lastXY)); + } + } + + // Recreate the brushes - behaviorservice calls this when the user preferences changes + public static void SyncBrushes() + { + s_hoverBrush.Dispose(); + s_hoverBrush = new SolidBrush(Color.FromArgb(50, SystemColors.Highlight)); + + s_selectionBorderBrush.Dispose(); + s_selectionBorderBrush = new HatchBrush(HatchStyle.Percent50, SystemColors.ControlDarkDark, Color.Transparent); + + SafeNativeMethods.DeleteObject(new HandleRef(null, s_grabHandleFillBrushPrimary)); + s_grabHandleFillBrushPrimary = SafeNativeMethods.CreateSolidBrush(ColorTranslator.ToWin32(SystemColors.Window)); + + SafeNativeMethods.DeleteObject(new HandleRef(null, s_grabHandleFillBrush)); + s_grabHandleFillBrush = SafeNativeMethods.CreateSolidBrush(ColorTranslator.ToWin32(SystemColors.ControlText)); + + SafeNativeMethods.DeleteObject(new HandleRef(null, s_grabHandlePenPrimary)); + s_grabHandlePenPrimary = SafeNativeMethods.CreatePen(NativeMethods.PS_SOLID, 1, ColorTranslator.ToWin32(SystemColors.ControlText)); + + SafeNativeMethods.DeleteObject(new HandleRef(null, s_grabHandlePen)); + s_grabHandlePen = SafeNativeMethods.CreatePen(NativeMethods.PS_SOLID, 1, ColorTranslator.ToWin32(SystemColors.Window)); + } + + /// + /// Draws a ControlDarkDark border around the given image. + /// + private static void DrawDragBorder(Graphics g, Size imageSize, int borderSize, Color backColor) + { + Pen pen = SystemPens.ControlDarkDark; + if (backColor != Color.Empty && backColor.GetBrightness() < .5) + { + pen = SystemPens.ControlLight; + } + + //draw a border w/o the corners connecting + g.DrawLine(pen, 1, 0, imageSize.Width - 2, 0); + g.DrawLine(pen, 1, imageSize.Height - 1, imageSize.Width - 2, imageSize.Height - 1); + g.DrawLine(pen, 0, 1, 0, imageSize.Height - 2); + g.DrawLine(pen, imageSize.Width - 1, 1, imageSize.Width - 1, imageSize.Height - 2); + + //loop through drawing inner-rects until we get the proper thickness + for (int i = 1; i < borderSize; i++) + { + g.DrawRectangle(pen, i, i, imageSize.Width - (2 + i), imageSize.Height - (2 + i)); + } + } + + /// + /// Used for drawing the borders around controls that are being resized + /// + public static void DrawResizeBorder(Graphics g, Region resizeBorder, Color backColor) + { + Brush brush = SystemBrushes.ControlDarkDark; + if (backColor != Color.Empty && backColor.GetBrightness() < .5) + { + brush = SystemBrushes.ControlLight; + } + + g.FillRegion(brush, resizeBorder); + } + + /// + /// Used for drawing the frame when doing a mouse drag + /// + public static void DrawFrame(Graphics g, Region resizeBorder, FrameStyle style, Color backColor) + { + Brush brush; + Color color = SystemColors.ControlDarkDark; + if (backColor != Color.Empty && backColor.GetBrightness() < .5) + { + color = SystemColors.ControlLight; + } + switch (style) + { + case FrameStyle.Dashed: + brush = new HatchBrush(HatchStyle.Percent50, color, Color.Transparent); + break; + case FrameStyle.Thick: + default: + brush = new SolidBrush(color); + break; + } + g.FillRegion(brush, resizeBorder); + brush.Dispose(); + } + + /// + /// Used for drawing the grabhandles around sizeable selected controls and components. + /// + public static void DrawGrabHandle(Graphics graphics, Rectangle bounds, bool isPrimary, Glyph glyph) + { + IntPtr hDC = graphics.GetHdc(); + try + { + //set our pen and brush based on primary selection + IntPtr oldBrush = SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, isPrimary ? s_grabHandleFillBrushPrimary : s_grabHandleFillBrush)); + IntPtr oldPen = SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, isPrimary ? s_grabHandlePenPrimary : s_grabHandlePen)); + + //draw our rounded rect grabhandle + SafeNativeMethods.RoundRect(new HandleRef(glyph, hDC), bounds.Left, bounds.Top, bounds.Right, bounds.Bottom, 2, 2); + //restore old pen and brush + SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, oldBrush)); + SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, oldPen)); + } + + finally + { + graphics.ReleaseHdcInternal(hDC); + } + } + + /// + /// Used for drawing the no-resize handle for non-resizeable selected controls and components. + /// + public static void DrawNoResizeHandle(Graphics graphics, Rectangle bounds, bool isPrimary, Glyph glyph) + { + IntPtr hDC = graphics.GetHdc(); + try + { + //set our pen and brush based on primary selection + IntPtr oldBrush = SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, isPrimary ? s_grabHandleFillBrushPrimary : s_grabHandleFillBrush)); + IntPtr oldPen = SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, s_grabHandlePenPrimary)); + + //draw our rect no-resize handle + SafeNativeMethods.Rectangle(new HandleRef(glyph, hDC), bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); + //restore old pen and brush + SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, oldBrush)); + SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, oldPen)); + } + finally + { + graphics.ReleaseHdcInternal(hDC); + } + } + + /// + /// Used for drawing the lock handle for locked selected controls and components. + /// + public static void DrawLockedHandle(Graphics graphics, Rectangle bounds, bool isPrimary, Glyph glyph) + { + IntPtr hDC = graphics.GetHdc(); + try + { + IntPtr oldPen = SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, s_grabHandlePenPrimary)); + // Upper rect - upper rect is always filled with the primary brush + IntPtr oldBrush = SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, s_grabHandleFillBrushPrimary)); + SafeNativeMethods.RoundRect(new HandleRef(glyph, hDC), bounds.Left + LOCKHANDLEUPPER_OFFSET, bounds.Top, + bounds.Left + LOCKHANDLEUPPER_OFFSET + LOCKHANDLESIZE_UPPER, bounds.Top + LOCKHANDLESIZE_UPPER, 2, 2); + // Lower rect - its fillbrush depends on the primary selection + SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, isPrimary ? s_grabHandleFillBrushPrimary : s_grabHandleFillBrush)); + SafeNativeMethods.Rectangle(new HandleRef(glyph, hDC), bounds.Left, bounds.Top + LOCKHANDLELOWER_OFFSET, bounds.Right, bounds.Bottom); + + //restore old pen and brush + SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, oldBrush)); + SafeNativeMethods.SelectObject(new HandleRef(glyph, hDC), new HandleRef(glyph, oldPen)); + } + finally + { + graphics.ReleaseHdcInternal(hDC); + } + } + + /// + /// Uses the lockedBorderBrush to draw a 'locked' border on the given Graphics at the specified bounds. + /// + public static void DrawSelectionBorder(Graphics graphics, Rectangle bounds) + { + graphics.FillRectangle(s_selectionBorderBrush, bounds); + } + + /// + /// Used to generate an image that represents the given control. First, this method will call the 'GenerateSnapShotWithWM_PRINT' method on the control. If we believe that this method did not return us a valid image (caused by some comctl/ax controls not properly responding to a wm_print) then we will attempt to do a bitblt of the control instead. + /// + public static void GenerateSnapShot(Control control, ref Image image, int borderSize, double opacity, Color backColor) + { + //GenerateSnapShot will return a boolean value indicating if the control returned an image or not... + if (!GenerateSnapShotWithWM_PRINT(control, ref image)) + { + //here, we failed to get the image on wmprint - so try bitblt + GenerateSnapShotWithBitBlt(control, ref image); + //if we still failed - we'll just fall though, put up a border around an empty area and call it good enough + } + //set the opacity + if (opacity < 1.0 && opacity > 0.0) + { + // make this semi-transparent + SetImageAlpha((Bitmap)image, opacity); + } + + // draw a drag border around this thing + if (borderSize > 0) + { + using (Graphics g = Graphics.FromImage(image)) + { + DrawDragBorder(g, image.Size, borderSize, backColor); + } + } + } + + /// + /// Retrieves the width and height of a selection border grab handle. Designers may need this to properly position their user interfaces. + /// + public static Size GetAdornmentDimensions(AdornmentType adornmentType) + { + switch (adornmentType) + { + case AdornmentType.GrabHandle: + return new Size(HANDLESIZE, HANDLESIZE); + case AdornmentType.ContainerSelector: + case AdornmentType.Maximum: + return new Size(CONTAINERGRABHANDLESIZE, CONTAINERGRABHANDLESIZE); + } + return new Size(0, 0); + } + + public static bool UseSnapLines(IServiceProvider provider) + { + bool useSnapLines = true; + object optionValue = null; + if (provider.GetService(typeof(DesignerOptionService)) is DesignerOptionService options) + { + PropertyDescriptor snaplinesProp = options.Options.Properties["UseSnapLines"]; + if (snaplinesProp != null) + { + optionValue = snaplinesProp.GetValue(null); + } + } + + if (optionValue != null && optionValue is bool) + { + useSnapLines = (bool)optionValue; + } + return useSnapLines; + } + + public static object GetOptionValue(IServiceProvider provider, string name) + { + object optionValue = null; + if (provider != null) + { + if (provider.GetService(typeof(DesignerOptionService)) is DesignerOptionService desOpts) + { + PropertyDescriptor prop = desOpts.Options.Properties[name]; + if (prop != null) + { + optionValue = prop.GetValue(null); + } + } + else + { + if (provider.GetService(typeof(IDesignerOptionService)) is IDesignerOptionService optSvc) + { + optionValue = optSvc.GetOptionValue("WindowsFormsDesigner\\General", name); + } + } + } + return optionValue; + } + + /// + /// Uses BitBlt to geta snapshot of the control + /// + public static void GenerateSnapShotWithBitBlt(Control control, ref Image image) + { + //get the DC's and create our image + HandleRef hWnd = new HandleRef(control, control.Handle); + IntPtr controlDC = UnsafeNativeMethods.GetDC(hWnd); + image = new Bitmap(Math.Max(control.Width, MINCONTROLBITMAPSIZE), Math.Max(control.Height, MINCONTROLBITMAPSIZE), System.Drawing.Imaging.PixelFormat.Format32bppPArgb); + + using (Graphics gDest = Graphics.FromImage(image)) + { + if (control.BackColor == Color.Transparent) + { + gDest.Clear(SystemColors.Control); + } + IntPtr destDC = gDest.GetHdc(); + //perform our bitblit operation to push the image into the dest bitmap + SafeNativeMethods.BitBlt(destDC, 0, 0, image.Width, image.Height, controlDC, 0, 0, 0xcc0020/*RasterOp.SOURCE*/); + //clean up all our handles and what not + gDest.ReleaseHdc(destDC); + } + } + + /// + /// Uses WM_PRINT to get a snapshot of the control. This method will return true if the control properly responded to the wm_print message. + /// + public static bool GenerateSnapShotWithWM_PRINT(Control control, ref Image image) + { + IntPtr hWnd = control.Handle; + image = new Bitmap(Math.Max(control.Width, MINCONTROLBITMAPSIZE), Math.Max(control.Height, MINCONTROLBITMAPSIZE), System.Drawing.Imaging.PixelFormat.Format32bppPArgb); + + //Have to do this BEFORE we set the testcolor. + if (control.BackColor == Color.Transparent) + { + using (Graphics g = Graphics.FromImage(image)) + { + g.Clear(SystemColors.Control); + } + } + + // To validate that the control responded to the wm_print message, we pre-populate the bitmap with a colored center pixel. We assume that the control _did not_ respond to wm_print if these center pixel is still this value + Color testColor = Color.FromArgb(255, 252, 186, 238); + ((Bitmap)image).SetPixel(image.Width / 2, image.Height / 2, testColor); + using (Graphics g = Graphics.FromImage(image)) + { + IntPtr hDc = g.GetHdc(); + //send the actual wm_print message + NativeMethods.SendMessage(hWnd, NativeMethods.WM_PRINT, hDc, (IntPtr)(NativeMethods.PRF_CHILDREN | NativeMethods.PRF_CLIENT | NativeMethods.PRF_ERASEBKGND | NativeMethods.PRF_NONCLIENT)); + g.ReleaseHdc(hDc); + } + + //now check to see if our center pixel was cleared, if not then our wm_print failed + if (((Bitmap)image).GetPixel(image.Width / 2, image.Height / 2).Equals(testColor)) + { + //wm_print failed + return false; + } + return true; + } + + /// + /// Used by the Glyphs and ComponentTray to determine the Top, Left, Right, Bottom and Body bound rects related to their original bounds and bordersize. + /// + public static Rectangle GetBoundsForSelectionType(Rectangle originalBounds, SelectionBorderGlyphType type, int borderSize) + { + Rectangle bounds = Rectangle.Empty; + switch (type) + { + case SelectionBorderGlyphType.Top: + bounds = new Rectangle(originalBounds.Left - borderSize, originalBounds.Top - borderSize, originalBounds.Width + 2 * borderSize, borderSize); + break; + case SelectionBorderGlyphType.Bottom: + bounds = new Rectangle(originalBounds.Left - borderSize, originalBounds.Bottom, originalBounds.Width + 2 * borderSize, borderSize); + break; + case SelectionBorderGlyphType.Left: + bounds = new Rectangle(originalBounds.Left - borderSize, originalBounds.Top - borderSize, borderSize, originalBounds.Height + 2 * borderSize); + break; + case SelectionBorderGlyphType.Right: + bounds = new Rectangle(originalBounds.Right, originalBounds.Top - borderSize, borderSize, originalBounds.Height + 2 * borderSize); + break; + case SelectionBorderGlyphType.Body: + bounds = originalBounds; + break; + } + return bounds; + } + + /// + /// Used by the Glyphs and ComponentTray to determine the Top, Left, Right, Bottom and Body bound rects related to their original bounds and bordersize. + /// Offset - how many pixels between the border glyph and the control + /// + private static Rectangle GetBoundsForSelectionType(Rectangle originalBounds, SelectionBorderGlyphType type, int bordersize, int offset) + { + Rectangle bounds = GetBoundsForSelectionType(originalBounds, type, bordersize); + if (offset != 0) + { + switch (type) + { + case SelectionBorderGlyphType.Top: + bounds.Offset(-offset, -offset); + bounds.Width += 2 * offset; + break; + case SelectionBorderGlyphType.Bottom: + bounds.Offset(-offset, offset); + bounds.Width += 2 * offset; + break; + case SelectionBorderGlyphType.Left: + bounds.Offset(-offset, -offset); + bounds.Height += 2 * offset; + break; + case SelectionBorderGlyphType.Right: + bounds.Offset(offset, -offset); + bounds.Height += 2 * offset; + break; + case SelectionBorderGlyphType.Body: + bounds = originalBounds; + break; + } + } + return bounds; + } + + /// + /// Used by the Glyphs and ComponentTray to determine the Top, Left, Right, Bottom and Body bound rects related to their original bounds and bordersize. + /// + public static Rectangle GetBoundsForSelectionType(Rectangle originalBounds, SelectionBorderGlyphType type) + { + return GetBoundsForSelectionType(originalBounds, type, DesignerUtils.SELECTIONBORDERSIZE, SELECTIONBORDEROFFSET); + } + + public static Rectangle GetBoundsForNoResizeSelectionType(Rectangle originalBounds, SelectionBorderGlyphType type) + { + return GetBoundsForSelectionType(originalBounds, type, DesignerUtils.SELECTIONBORDERSIZE, NORESIZEBORDEROFFSET); + } + + /// + /// Identifes where the text baseline for our control which should be based on bounds, padding, font, and textalignment. + /// + public static int GetTextBaseline(Control ctrl, ContentAlignment alignment) + { + //determine the actual client area we are working in (w/padding) + Rectangle face = ctrl.ClientRectangle; + + //get the font metrics via gdi + int fontAscent = 0; + int fontHeight = 0; + using (Graphics g = ctrl.CreateGraphics()) + { + IntPtr dc = g.GetHdc(); + IntPtr hFont = ctrl.Font.ToHfont(); + IntPtr hFontOld; + try + { + hFontOld = SafeNativeMethods.SelectObject(new HandleRef(ctrl, dc), new HandleRef(ctrl, hFont)); + NativeMethods.TEXTMETRIC metrics = new NativeMethods.TEXTMETRIC(); + UnsafeNativeMethods.GetTextMetrics(new HandleRef(ctrl, dc), metrics); + //add the font ascent to the baseline + fontAscent = metrics.tmAscent + 1; + fontHeight = metrics.tmHeight; + SafeNativeMethods.SelectObject(new HandleRef(ctrl, dc), new HandleRef(ctrl, hFontOld)); + } + finally + { + SafeNativeMethods.DeleteObject(new HandleRef(ctrl.Font, hFont)); + g.ReleaseHdc(dc); + } + } + + //now add it all up + if ((alignment & anyTopAlignment) != 0) + { + return face.Top + fontAscent; + } + else if ((alignment & anyMiddleAlignment) != 0) + { + return face.Top + (face.Height / 2) - (fontHeight / 2) + fontAscent; + } + else + { + return face.Bottom - fontHeight + fontAscent; + } + } + + /// + /// Called by the ParentControlDesigner when creating a new + /// control - this will update the new control's bounds with the + /// proper toolbox/snapline information that has been stored + /// off + // + /// isMirrored - Is the ParentControlDesigner mirrored? If so, we need + /// to offset for that. This is because all snapline stuff is done + /// using a LTR coordinate system + /// + public static Rectangle GetBoundsFromToolboxSnapDragDropInfo(ToolboxSnapDragDropEventArgs e, Rectangle originalBounds, bool isMirrored) + { + Rectangle newBounds = originalBounds; + + //this should always be the case 'cause we don't + //create 'e' unless we have an offset + if (e.Offset != Point.Empty) + { + //snap either up or down depending on offset + if ((e.SnapDirections & ToolboxSnapDragDropEventArgs.SnapDirection.Top) != 0) + { + newBounds.Y += e.Offset.Y;//snap to top - so move up our bounds + } + else if ((e.SnapDirections & ToolboxSnapDragDropEventArgs.SnapDirection.Bottom) != 0) + { + newBounds.Y = originalBounds.Y - originalBounds.Height + e.Offset.Y; + } + + //snap either left or right depending on offset + if (!isMirrored) + { + if ((e.SnapDirections & ToolboxSnapDragDropEventArgs.SnapDirection.Left) != 0) + { + newBounds.X += e.Offset.X;//snap to left- + } + else if ((e.SnapDirections & ToolboxSnapDragDropEventArgs.SnapDirection.Right) != 0) + { + newBounds.X = originalBounds.X - originalBounds.Width + e.Offset.X; + } + } + else + { + // ParentControlDesigner is RTL, that means that the origin is upper-right, not upper-left + if ((e.SnapDirections & ToolboxSnapDragDropEventArgs.SnapDirection.Left) != 0) + { + // e.Offset.X is negative when we snap to left + newBounds.X = originalBounds.X - originalBounds.Width - e.Offset.X; + } + else if ((e.SnapDirections & ToolboxSnapDragDropEventArgs.SnapDirection.Right) != 0) + { + // e.Offset.X is positive when we snao to right + newBounds.X -= e.Offset.X; + } + } + } + + return newBounds; + } + + /// + /// Determine a unique site name for a component, starting from a base name. Return value should be passed into the Container.Add() method. If null is returned, this just means "let container generate a default name based on component type". + /// + public static string GetUniqueSiteName(IDesignerHost host, string name) + { + // Item has no explicit name, so let host generate a type-based name instead + if (string.IsNullOrEmpty(name)) + { + return null; + } + + // Get the name creation service from the designer host + INameCreationService nameCreationService = (INameCreationService)host.GetService(typeof(INameCreationService)); + if (nameCreationService == null) + { + return null; + } + + // See if desired name is already in use + object existingComponent = host.Container.Components[name]; + if (existingComponent == null) + { + // Name is not in use - but make sure that it contains valid characters before using it! + return nameCreationService.IsValidName(name) ? name : null; + } + else + { + // Name is in use (and therefore basically valid), so start appending numbers + string nameN = name; + for (int i = 1; !nameCreationService.IsValidName(nameN); ++i) + { + nameN = name + i.ToString(CultureInfo.InvariantCulture); + } + return nameN; + } + } + + /// + /// Applies the given opacity to the image + /// + private static unsafe void SetImageAlpha(Bitmap b, double opacity) + { + if (opacity == 1.0) + { + return; + } + + byte[] alphaValues = new byte[256]; + // precompute all the possible alpha values into an array so we don't do multiplications in the loop + for (int i = 0; i < alphaValues.Length; i++) + { + alphaValues[i] = (byte)(i * opacity); + } + + // lock the data in ARGB format. + // + BitmapData data = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + try + { + // compute the number of pixels that we're modifying. + int pixels = data.Height * data.Width; + int* pPixels = (int*)data.Scan0; + + // have the compiler figure out where to stop for us + // by doing the pointer math + byte* maxAddr = (byte*)(pPixels + pixels); + + // now run through the pixels only modifyng the A byte + for (byte* addr = (byte*)(pPixels) + 3; addr < maxAddr; addr += 4) + { + // the new value is just an index into our precomputed value array from above. + *addr = alphaValues[*addr]; + } + } + finally + { + // now, apply the data back to the bitmap. + b.UnlockBits(data); + } + } + + /// + /// This method removes types that are generics from the input collection + /// + public static ICollection FilterGenericTypes(ICollection types) + { + if (types == null || types.Count == 0) + return types; + + //now we get each Type and add it to the destination collection if its not a generic + ArrayList final = new ArrayList(types.Count); + foreach (Type t in types) + { + if (!t.ContainsGenericParameters) + { + final.Add(t); + } + } + return final; + } + + /// + /// Checks the given container, substituting any nested container with its owning container. Ensures that a SplitterPanel in a SplitContainer returns the same container as other form components, since SplitContainer sites its two SplitterPanels inside a nested container. + /// + public static IContainer CheckForNestedContainer(IContainer container) + { + if (container is NestedContainer nestedContainer) + { + return nestedContainer.Owner.Site.Container; + } + else + { + return container; + } + } + + /// + /// Used to create copies of the objects that we are dragging in a drag operation + /// + public static ICollection CopyDragObjects(ICollection objects, IServiceProvider svcProvider) + { + if (objects == null || svcProvider == null) + { + Debug.Fail("Invalid parameter passed to DesignerUtils.CopyObjects."); + return null; + } + + Cursor oldCursor = Cursor.Current; + try + { + Cursor.Current = Cursors.WaitCursor; + ComponentSerializationService css = svcProvider.GetService(typeof(ComponentSerializationService)) as ComponentSerializationService; + IDesignerHost host = svcProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; + Debug.Assert(css != null, "No component serialization service -- we cannot copy the objects"); + Debug.Assert(host != null, "No host -- we cannot copy the objects"); + if (css != null && host != null) + { + SerializationStore store = null; + store = css.CreateStore(); + // Get all the objects, meaning we want the children too + ICollection copyObjects = GetCopySelection(objects, host); + + // The serialization service does not (yet) handle serializing collections + foreach (IComponent comp in copyObjects) + { + css.Serialize(store, comp); + } + store.Close(); + copyObjects = css.Deserialize(store); + + // Now, copyObjects contains a flattened list of all the controls contained in the original drag objects, that's not what we want to return. We only want to return the root drag objects, so that the caller gets an identical copy - identical in terms of objects.Count + ArrayList newObjects = new ArrayList(objects.Count); + foreach (IComponent comp in copyObjects) + { + Control c = comp as Control; + if (c != null && c.Parent == null) + { + newObjects.Add(comp); + } + else if (c == null) + { // this happens when we are dragging a toolstripitem + // TODO: Can we remove the ToolStrip specific code? + if (comp is ToolStripItem item && item.GetCurrentParent() == null) + { + newObjects.Add(comp); + } + } + } + Debug.Assert(newObjects.Count == objects.Count, "Why is the count of the copied objects not the same?"); + return newObjects; + } + } + finally + { + Cursor.Current = oldCursor; + } + return null; + } + + private static ICollection GetCopySelection(ICollection objects, IDesignerHost host) + { + if (objects == null || host == null) + { + return null; + } + + ArrayList copySelection = new ArrayList(); + foreach (IComponent comp in objects) + { + copySelection.Add(comp); + GetAssociatedComponents(comp, host, copySelection); + } + return copySelection; + } + + internal static void GetAssociatedComponents(IComponent component, IDesignerHost host, ArrayList list) + { + if (host == null) + { + return; + } + + if (!(host.GetDesigner(component) is ComponentDesigner designer)) + { + return; + } + + foreach (IComponent childComp in designer.AssociatedComponents) + { + if (childComp.Site != null) + { + list.Add(childComp); + GetAssociatedComponents(childComp, host, list); + } + } + } + + private static int TreeView_GetExtendedStyle(IntPtr handle) + { + IntPtr ptr = NativeMethods.SendMessage(handle, NativeMethods.TVM_GETEXTENDEDSTYLE, IntPtr.Zero, IntPtr.Zero); + return ptr.ToInt32(); + } + + private static void TreeView_SetExtendedStyle(IntPtr handle, int extendedStyle, int mask) + { + NativeMethods.SendMessage(handle, NativeMethods.TVM_SETEXTENDEDSTYLE, new IntPtr(mask), new IntPtr(extendedStyle)); + } + + /// + /// Modify a WinForms TreeView control to use the new Explorer style theme + /// + /// The tree view control to modify + public static void ApplyTreeViewThemeStyles(TreeView treeView) + { + if (treeView == null) + { + throw new ArgumentNullException("treeView"); + } + treeView.HotTracking = true; + treeView.ShowLines = false; + IntPtr hwnd = treeView.Handle; + SafeNativeMethods.SetWindowTheme(hwnd, "Explorer", null); + int exstyle = TreeView_GetExtendedStyle(hwnd); + exstyle |= NativeMethods.TVS_EX_DOUBLEBUFFER | NativeMethods.TVS_EX_FADEINOUTEXPANDOS; + TreeView_SetExtendedStyle(hwnd, exstyle, 0); + } + + private static void ListView_SetExtendedListViewStyleEx(IntPtr handle, int mask, int extendedStyle) + { + NativeMethods.SendMessage(handle, NativeMethods.LVM_SETEXTENDEDLISTVIEWSTYLE, new IntPtr(mask), new IntPtr(extendedStyle)); + } + + /// + /// Modify a WinForms ListView control to use the new Explorer style theme + /// + /// The list view control to modify + public static void ApplyListViewThemeStyles(ListView listView) + { + if (listView == null) + { + throw new ArgumentNullException("listView"); + } + IntPtr hwnd = listView.Handle; + SafeNativeMethods.SetWindowTheme(hwnd, "Explorer", null); + ListView_SetExtendedListViewStyleEx(hwnd, NativeMethods.LVS_EX_DOUBLEBUFFER, NativeMethods.LVS_EX_DOUBLEBUFFER); + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerVerbToolStripMenuItem.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerVerbToolStripMenuItem.cs new file mode 100644 index 00000000000..308325e89e0 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerVerbToolStripMenuItem.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel.Design; + +namespace System.Windows.Forms.Design +{ + /// + /// Associates DesignerVerb with ToolStripMenuItem. + /// + internal class DesignerVerbToolStripMenuItem : ToolStripMenuItem + { + readonly DesignerVerb _verb; + + // Text is a virtual method on the base class, but since we don't override it we should be okay. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public DesignerVerbToolStripMenuItem(DesignerVerb verb) + { + _verb = verb; + Text = verb.Text; + RefreshItem(); + } + + public void RefreshItem() + { + if (_verb != null) + { + Visible = _verb.Visible; + Enabled = _verb.Enabled; + Checked = _verb.Checked; + } + } + + protected override void OnClick(System.EventArgs e) + { + if (_verb != null) + { + _verb.Invoke(); + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DocumentDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DocumentDesigner.cs index bfd83510e58..6d6e677e78c 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DocumentDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DocumentDesigner.cs @@ -108,6 +108,43 @@ protected override void OnCreateHandle() throw new NotImplementedException(SR.NotImplementedByDesign); } + internal virtual void DoProperMenuSelection(ICollection selComponents) + { + foreach (object obj in selComponents) + { + if (obj is ContextMenu cm) + { + menuEditorService.SetMenu((Menu)obj); + } + else + { + if (obj is MenuItem item) + { + //before we set the selection, we need to check if the item belongs the current menu, if not, we need to set the menu editor to the appropiate menu, then set selection + MenuItem parent = item; + while (parent.Parent is MenuItem) + { + parent = (MenuItem)parent.Parent; + } + + if (menuEditorService.GetMenu() != parent.Parent) + { + menuEditorService.SetMenu(parent.Parent); + } + + //ok, here we have the correct editor selected for this item. Now, if there's only one item selected, then let the editor service know, if there is more than one - then the selection was done through themenu editor and we don't need to tell it + if (selComponents.Count == 1) + { + menuEditorService.SetSelection(item); + } + } + //Here, something is selected, but the menuservice isn't interested so, we'll collapse our active menu accordingly + else + menuEditorService.SetMenu(null); + } + } + } + /// /// Determines if a MenuEditorService has already been started. If not, /// this method will create a new instance of the service. diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/EditorServiceContext.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/EditorServiceContext.cs new file mode 100644 index 00000000000..9a7d8a0c80b --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/EditorServiceContext.cs @@ -0,0 +1,203 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing.Design; + +namespace System.Windows.Forms.Design +{ + /// + /// Provides an implementation of IWindowsFormsEditorService and ITypeDescriptorContext. Also provides a static method to invoke a UITypeEditor given a designer, an object and a property name. + /// + internal class EditorServiceContext : IWindowsFormsEditorService, ITypeDescriptorContext + { + private readonly ComponentDesigner _designer; + private IComponentChangeService _componentChangeSvc; + private readonly PropertyDescriptor _targetProperty; + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal EditorServiceContext(ComponentDesigner designer) + { + _designer = designer; + } + + internal EditorServiceContext(ComponentDesigner designer, PropertyDescriptor prop) + { + _designer = designer; + _targetProperty = prop; + if (prop == null) + { + prop = TypeDescriptor.GetDefaultProperty(designer.Component); + if (prop != null && typeof(ICollection).IsAssignableFrom(prop.PropertyType)) + { + _targetProperty = prop; + } + } + Debug.Assert(_targetProperty != null, "Need PropertyDescriptor for ICollection property to associate collectoin edtior with."); + } + + internal EditorServiceContext(ComponentDesigner designer, PropertyDescriptor prop, string newVerbText) : this(designer, prop) + { + Debug.Assert(!string.IsNullOrEmpty(newVerbText), "newVerbText cannot be null or empty"); + _designer.Verbs.Add(new DesignerVerb(newVerbText, new EventHandler(OnEditItems))); + } + + public static object EditValue(ComponentDesigner designer, object objectToChange, string propName) + { + // Get PropertyDescriptor + PropertyDescriptor descriptor = TypeDescriptor.GetProperties(objectToChange)[propName]; + // Create a Context + EditorServiceContext context = new EditorServiceContext(designer, descriptor); + // Get Editor + UITypeEditor editor = descriptor.GetEditor(typeof(UITypeEditor)) as UITypeEditor; + // Get value to edit + object value = descriptor.GetValue(objectToChange); + // Edit value + object newValue = editor.EditValue(context, context, value); + + if (newValue != value) + { + try + { + descriptor.SetValue(objectToChange, newValue); + } + catch (CheckoutException) + { + + } + } + return newValue; + } + + /// + /// Our caching property for the IComponentChangeService + /// + private IComponentChangeService ChangeService + { + get + { + if (_componentChangeSvc == null) + { + _componentChangeSvc = (IComponentChangeService)((IServiceProvider)this).GetService(typeof(IComponentChangeService)); + } + return _componentChangeSvc; + } + } + + /// + /// Self-explanitory interface impl. + /// + IContainer ITypeDescriptorContext.Container + { + get + { + if (_designer.Component.Site != null) + { + return _designer.Component.Site.Container; + } + return null; + } + } + + /// + /// Self-explanitory interface impl. + /// + void ITypeDescriptorContext.OnComponentChanged() + { + ChangeService.OnComponentChanged(_designer.Component, _targetProperty, null, null); + } + + /// + /// Self-explanitory interface impl. + /// + bool ITypeDescriptorContext.OnComponentChanging() + { + try + { + ChangeService.OnComponentChanging(_designer.Component, _targetProperty); + } + catch (CheckoutException checkoutException) + { + if (checkoutException == CheckoutException.Canceled) + { + return false; + } + throw; + } + return true; + } + + object ITypeDescriptorContext.Instance + { + get => _designer.Component; + } + + PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor + { + get => _targetProperty; + } + + object IServiceProvider.GetService(Type serviceType) + { + if (serviceType == typeof(ITypeDescriptorContext) || serviceType == typeof(IWindowsFormsEditorService)) + { + return this; + } + if (_designer.Component != null && _designer.Component.Site != null) + { + return _designer.Component.Site.GetService(serviceType); + } + return null; + } + + void IWindowsFormsEditorService.CloseDropDown() + { + // we'll never be called to do this. + Debug.Fail("NOTIMPL"); + return; + } + + void IWindowsFormsEditorService.DropDownControl(Control control) + { + Debug.Fail("NOTIMPL"); + return; + } + + System.Windows.Forms.DialogResult IWindowsFormsEditorService.ShowDialog(Form dialog) + { + IUIService uiSvc = (IUIService)((IServiceProvider)this).GetService(typeof(IUIService)); + if (uiSvc != null) + { + return uiSvc.ShowDialog(dialog); + } + else + { + return dialog.ShowDialog(_designer.Component as IWin32Window); + } + } + + /// + /// When the verb is invoked, use all the stuff above to show the dialog, etc. + /// + private void OnEditItems(object sender, EventArgs e) + { + object propertyValue = _targetProperty.GetValue(_designer.Component); + if (propertyValue == null) + { + return; + } + CollectionEditor itemsEditor = TypeDescriptor.GetEditor(propertyValue, typeof(UITypeEditor)) as CollectionEditor; + + Debug.Assert(itemsEditor != null, "Didn't get a collection editor for type '" + _targetProperty.PropertyType.FullName + "'"); + if (itemsEditor != null) + { + itemsEditor.EditValue(this, this, propertyValue); + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs new file mode 100644 index 00000000000..d307af9671b --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs @@ -0,0 +1,730 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Drawing; +using System.Globalization; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// The FormDocumentDesigner class builds on the DocumentDesigner. It adds shadowing for form properties that need to be shadowed and it also adds logic to properly paint the form's title bar to match the active document window. + /// + internal class FormDocumentDesigner : DocumentDesigner + { + private Size _autoScaleBaseSize = Size.Empty; + private bool _inAutoscale = false; + private int _heightDelta = 0; + private bool _isMenuInherited; //indicates if the 'active menu' is inherited + private bool _hasMenu = false; + private InheritanceAttribute _inheritanceAttribute; + private bool _initializing = false; + private bool _autoSize = false; + private ToolStripAdornerWindowService _toolStripAdornerWindowService = null; + + /// + /// Shadow the AcceptButton property at design-time so that we can preserve it when the form is rebuilt. Otherwise, form.Controls.Clear() will clear it out when we don't want it to. + /// + private IButtonControl AcceptButton + { + get => ShadowProperties["AcceptButton"] as IButtonControl; + set + { + ((Form)Component).AcceptButton = value; + ShadowProperties["AcceptButton"] = value; + } + } + + /// + /// Shadow the CancelButton property at design-time so that we can preserve it when the form is rebuilt. Otherwise, form.Controls.Clear() will clear it out when we don't want it to. + /// + private IButtonControl CancelButton + { + get => ShadowProperties["CancelButton"] as IButtonControl; + set + { + ((Form)Component).CancelButton = value; + ShadowProperties["CancelButton"] = value; + } + } + + /// + /// Shadowed version of the AutoScaleBaseSize property. We shadow this so that it always persists. Normally only properties that differ from the default values at instantiation are persisted, but this should always be written. So, we shadow it and add our own ShouldSerialize method. + /// + private Size AutoScaleBaseSize + { + get + { + // we don't want to get inherited value from a base form that might have been designed in a different DPI so we recalculate the thing instead of getting AutoScaleBaseSize (QFE 2280) +#pragma warning disable 618 + SizeF real = Form.GetAutoScaleSize(((Form)Component).Font); +#pragma warning restore 618 + return new Size((int)Math.Round(real.Width), (int)Math.Round(real.Height)); + } + set + { + // We do nothing at design time for this property; we always want to use the calculated value from the component. + _autoScaleBaseSize = value; + ShadowProperties["AutoScaleBaseSize"] = value; + } + } + + /// + /// We shadow the AutoSize property at design-time so that the form doesn't grow and shrink as users fiddle with autosize related properties. + /// + private bool AutoSize + { + get => _autoSize; + set => _autoSize = value; + } + + private bool ShouldSerializeAutoScaleBaseSize() + { + // Never serialize this unless AutoScale is turned on +#pragma warning disable 618 + return _initializing ? false + : ((Form)Component).AutoScale && ShadowProperties.Contains("AutoScaleBaseSize"); +#pragma warning restore 618 + } + + /// + /// Shadow property for the ClientSize property -- this allows us to intercept client size changes and apply the new menu height if necessary + /// + private Size ClientSize + { + get + { + if (_initializing) + { + return new Size(-1, -1); + } + else + { + Size size = new Size(-1, -1); + if (Component is Form form) + { + size = form.ClientSize; + // don't report the size decremented by the scroll bars, otherwise, we'll just lose that size when we run because the form doesn't take that into consideration (it's too early, it hasn't layed out and doesn't know it needs scrollbars) when sizing. + if (form.HorizontalScroll.Visible) + { + size.Height += SystemInformation.HorizontalScrollBarHeight; + } + if (form.VerticalScroll.Visible) + { + size.Width += SystemInformation.VerticalScrollBarWidth; + } + } + return size; + } + } + set + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null) + { + if (host.Loading) + { + _heightDelta = GetMenuHeight(); + } + } + ((Form)Component).ClientSize = value; + } + } + + /// + /// Shadow property for the IsMDIContainer property on a form. + /// + private bool IsMdiContainer + { + get => ((Form)Control).IsMdiContainer; + set + { + if (!value) + { + UnhookChildControls(Control); + } + ((Form)Control).IsMdiContainer = value; + if (value) + { + HookChildControls(Control); + } + } + } + + /// + /// Returns true if the active menu is an inherited component. We use this to determine if we need to resize the base control or not. + /// + private bool IsMenuInherited + { + get + { + if (_inheritanceAttribute == null && Menu != null) + { + _inheritanceAttribute = (InheritanceAttribute)TypeDescriptor.GetAttributes(Menu)[typeof(InheritanceAttribute)]; + if (_inheritanceAttribute.Equals(InheritanceAttribute.NotInherited)) + { + _isMenuInherited = false; + } + else + { + _isMenuInherited = true; + } + } + return _isMenuInherited; + } + } + + /// + /// Accessor method for the menu property on control. We shadow this property at design time. + /// + internal MainMenu Menu + { + get => (MainMenu)ShadowProperties["Menu"]; + set + { + if (value == ShadowProperties["Menu"]) + { + return; + } + ShadowProperties["Menu"] = value; + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null && !host.Loading) + { + EnsureMenuEditorService(value); + if (menuEditorService != null) + menuEditorService.SetMenu(value); + } + + if (_heightDelta == 0) + { + _heightDelta = GetMenuHeight(); + } + } + } + + /// + /// Opacity property on control. We shadow this property at design time. + /// + private double Opacity + { + get => (double)ShadowProperties["Opacity"]; + set + { + if (value < 0.0f || value > 1.0f) + { + throw new ArgumentException(string.Format(SR.InvalidBoundArgument, "value", value.ToString(CultureInfo.CurrentCulture), + (0.0f).ToString(CultureInfo.CurrentCulture), (1.0f).ToString(CultureInfo.CurrentCulture)), "value"); + } + ShadowProperties["Opacity"] = value; + } + } + + /// + /// Overrides the default implementation of ParentControlDesigner SnapLines. Note that if the Padding property is not set on our Form - we'll special case this and add default Padding values to our SnapLines. This was a usability request specific to the Form itself. Note that a Form only has Padding SnapLines. + /// + public override IList SnapLines + { + get + { + ArrayList snapLines = null; + base.AddPaddingSnapLines(ref snapLines); + if (snapLines == null) + { + Debug.Fail("why did base.AddPaddingSnapLines return null?"); + snapLines = new ArrayList(4); + } + + // if the padding has not been set - then we'll auto-add padding to form - this is a Usability request + if (Control.Padding == Padding.Empty && snapLines != null) + { + int paddingsFound = 0; // used to short-circuit once we find 4 paddings + for (int i = 0; i < snapLines.Count; i++) + { + // remove previous padding snaplines + if (snapLines[i] is SnapLine snapLine && snapLine.Filter != null && snapLine.Filter.StartsWith(SnapLine.Padding)) + { + if (snapLine.Filter.Equals(SnapLine.PaddingLeft) || snapLine.Filter.Equals(SnapLine.PaddingTop)) + { + snapLine.AdjustOffset(DesignerUtils.DEFAULTFORMPADDING); + paddingsFound++; + } + + if (snapLine.Filter.Equals(SnapLine.PaddingRight) || snapLine.Filter.Equals(SnapLine.PaddingBottom)) + { + snapLine.AdjustOffset(-DesignerUtils.DEFAULTFORMPADDING); + paddingsFound++; + } + + if (paddingsFound == 4) + { + break;//we adjusted all of our paddings + } + } + } + } + return snapLines; + } + } + + private Size Size + { + get => Control.Size; + set + { + IComponentChangeService cs = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(Component); + if (cs != null) + { + cs.OnComponentChanging(Component, props["ClientSize"]); + } + + Control.Size = value; + if (cs != null) + { + cs.OnComponentChanged(Component, props["ClientSize"], null, null); + } + } + } + + /// + /// Accessor method for the showInTaskbar property on control. We shadow this property at design time. + /// + private bool ShowInTaskbar + { + get => (bool)ShadowProperties["ShowInTaskbar"]; + set => ShadowProperties["ShowInTaskbar"] = value; + } + + /// + /// Accessor method for the windowState property on control. We shadow this property at design time. + /// + private FormWindowState WindowState + { + get => (FormWindowState)ShadowProperties["WindowState"]; + set => ShadowProperties["WindowState"] = value; + } + + private void ApplyAutoScaling(SizeF baseVar, Form form) + { + // We also don't do this if the property is empty. Otherwise we will perform two GetAutoScaleBaseSize calls only to find that they returned the same value. + if (!baseVar.IsEmpty) + { +#pragma warning disable 618 + SizeF newVarF = Form.GetAutoScaleSize(form.Font); +#pragma warning restore 618 + Size newVar = new Size((int)Math.Round(newVarF.Width), (int)Math.Round(newVarF.Height)); + // We save a significant amount of time by bailing early if there's no work to be done + if (baseVar.Equals(newVar)) + return; + float percY = ((float)newVar.Height) / ((float)baseVar.Height); + float percX = ((float)newVar.Width) / ((float)baseVar.Width); + try + { + _inAutoscale = true; +#pragma warning disable 618 + form.Scale(percX, percY); +#pragma warning restore 618 + } + finally + { + _inAutoscale = false; + } + } + } + + /// + /// Disposes of this designer. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + Debug.Assert(host != null, "Must have a designer host on dispose"); + if (host != null) + { + host.LoadComplete -= new EventHandler(OnLoadComplete); + host.Activated -= new EventHandler(OnDesignerActivate); + host.Deactivated -= new EventHandler(OnDesignerDeactivate); + } + + IComponentChangeService cs = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (cs != null) + { + cs.ComponentAdded -= new ComponentEventHandler(OnComponentAdded); + cs.ComponentRemoved -= new ComponentEventHandler(OnComponentRemoved); + } + } + base.Dispose(disposing); + } + + internal override void DoProperMenuSelection(ICollection selComponents) + { + foreach (object obj in selComponents) + { + //first check to see if our selection is any kind of menu: main, context, item AND the designer for the component is this one + if (obj is Menu menu) + { + //if it's a menu item, set the selection + if (menu is MenuItem item) + { + Menu currentMenu = menuEditorService.GetMenu(); + // before we set the selection, we need to check if the item belongs the current menu, if not, we need to set the menu editor to the appropiate menu, then set selection + MenuItem parent = item; + while (parent.Parent is MenuItem) + { + parent = (MenuItem)parent.Parent; + } + + if (!(currentMenu == parent.Parent)) + { + menuEditorService.SetMenu(parent.Parent); + } + + // ok, here we have the correct editor selected for this item. Now, if there's only one item selected, then let the editor service know, if there is more than one - then the selection was done through the menu editor and we don't need to tell it + if (selComponents.Count == 1) + { + menuEditorService.SetSelection(item); + } + } + // here, either it's a main or context menu, even if the menu is the current one, we still want to call this "SetMenu" method, 'cause that'll collapse it and remove the focus + else + { + menuEditorService.SetMenu(menu); + } + return; + } + // Here, something is selected, but it is in no way, shape, or form a menu so, we'll collapse our active menu accordingly + else + { + if (Menu != null && Menu.MenuItems.Count == 0) + { + menuEditorService.SetMenu(null); + } + else + { + menuEditorService.SetMenu(Menu); + } + NativeMethods.SendMessage(Control.Handle, NativeMethods.WM_NCACTIVATE, 1, 0); + } + } + } + + /// + /// Determines if a MenuEditorService has already been started. If not, this method will create a new instance of the service. We override this because we want to allow any kind of menu to start the service, not just ContextMenus. + /// + protected override void EnsureMenuEditorService(IComponent c) + { + if (menuEditorService == null && c is Menu) + { + menuEditorService = (IMenuEditorService)GetService(typeof(IMenuEditorService)); + } + } + + private void EnsureToolStripWindowAdornerService() + { + if (_toolStripAdornerWindowService == null) + { + _toolStripAdornerWindowService = (ToolStripAdornerWindowService)GetService(typeof(ToolStripAdornerWindowService)); + } + } + + /// + /// Gets the current menu height so we know how much to increment the form size by + /// + private int GetMenuHeight() + { + if (Menu == null || (IsMenuInherited && _initializing)) + { + return 0; + } + + if (menuEditorService != null) + { + // there is a magic property on teh menueditorservice that gives us this information. Unfortuantely, we can't compute it ourselves -- the menu shown in the designer isn't a windows one so we can't ask windows. + PropertyDescriptor heightProp = TypeDescriptor.GetProperties(menuEditorService)["MenuHeight"]; + if (heightProp != null) + { + int height = (int)heightProp.GetValue(menuEditorService); + return height; + } + } + return SystemInformation.MenuHeight; + } + + /// + /// Initializes the designer with the given component. The designer can get the component's site and request services from it in this call. + /// + public override void Initialize(IComponent component) + { + // We have to shadow the WindowState before we call base.Initialize + PropertyDescriptor windowStateProp = TypeDescriptor.GetProperties(component.GetType())["WindowState"]; + if (windowStateProp != null && windowStateProp.PropertyType == typeof(FormWindowState)) + { + WindowState = (FormWindowState)windowStateProp.GetValue(component); + } + + _initializing = true; + base.Initialize(component); + _initializing = false; + AutoResizeHandles = true; + Debug.Assert(component is Form, "FormDocumentDesigner expects its component to be a form."); + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null) + { + host.LoadComplete += new EventHandler(OnLoadComplete); + host.Activated += new EventHandler(OnDesignerActivate); + host.Deactivated += new EventHandler(OnDesignerDeactivate); + } + + Form form = (Form)Control; + form.WindowState = FormWindowState.Normal; + ShadowProperties["AcceptButton"] = form.AcceptButton; + ShadowProperties["CancelButton"] = form.CancelButton; + // Monitor component/remove add events for our tray + IComponentChangeService cs = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (cs != null) + { + cs.ComponentAdded += new ComponentEventHandler(OnComponentAdded); + cs.ComponentRemoved += new ComponentEventHandler(OnComponentRemoved); + } + } + + /// + /// Called when a component is added to the design container. If the component isn't a control, this will demand create the component tray and add the component to it. + /// + private void OnComponentAdded(object source, ComponentEventArgs ce) + { + if (ce.Component is Menu) + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null && !host.Loading) + { + //if it's a MainMenu & we don't have one set for the form yet, then do it... + if (ce.Component is MainMenu && !_hasMenu) + { + PropertyDescriptor menuProp = TypeDescriptor.GetProperties(Component)["Menu"]; + Debug.Assert(menuProp != null, "What the happened to the Menu property"); + menuProp.SetValue(Component, ce.Component); + _hasMenu = true; + } + } + } + if (ce.Component is ToolStrip && _toolStripAdornerWindowService == null) + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null) + { + EnsureToolStripWindowAdornerService(); + } + } + } + + /// + /// Called when a component is removed from the design container. Here, we check if a menu is being removed and handle removing the Form's mainmenu vs. other menus properly. + /// + private void OnComponentRemoved(object source, ComponentEventArgs ce) + { + if (ce.Component is Menu) + { + //if we deleted the form's mainmenu, set it null... + if (ce.Component == Menu) + { + PropertyDescriptor menuProp = TypeDescriptor.GetProperties(Component)["Menu"]; + Debug.Assert(menuProp != null, "What the happened to the Menu property"); + menuProp.SetValue(Component, null); + _hasMenu = false; + } + else if (menuEditorService != null && ce.Component == menuEditorService.GetMenu()) + { + menuEditorService.SetMenu(Menu); + } + } + if (ce.Component is ToolStrip && _toolStripAdornerWindowService != null) + { + _toolStripAdornerWindowService = null; + } + if (ce.Component is IButtonControl) + { + if (ce.Component == ShadowProperties["AcceptButton"]) + { + AcceptButton = null; + } + if (ce.Component == ShadowProperties["CancelButton"]) + { + CancelButton = null; + } + } + } + + /// + /// We're watching the handle creation in case we have a menu editor. If we do, the menu editor will have to be torn down and recreated. + /// + protected override void OnCreateHandle() + { + if (Menu != null && menuEditorService != null) + { + menuEditorService.SetMenu(null); + menuEditorService.SetMenu(Menu); + } + + if (_heightDelta != 0) + { + ((Form)Component).Height += _heightDelta; + _heightDelta = 0; + } + } + + // Called when our document becomes active. We paint our form's border the appropriate color here. + private void OnDesignerActivate(object source, EventArgs evevent) + { + // Paint the form's title bar UI-active + Control control = Control; + if (control != null && control.IsHandleCreated) + { + NativeMethods.SendMessage(control.Handle, NativeMethods.WM_NCACTIVATE, 1, 0); + SafeNativeMethods.RedrawWindow(control.Handle, null, IntPtr.Zero, NativeMethods.RDW_FRAME); + } + } + + /// + /// Called by the host when we become inactive. Here we update the title bar of our form so it's the inactive color. + /// + private void OnDesignerDeactivate(object sender, EventArgs e) + { + Control control = Control; + if (control != null && control.IsHandleCreated) + { + NativeMethods.SendMessage(control.Handle, NativeMethods.WM_NCACTIVATE, 0, 0); + SafeNativeMethods.RedrawWindow(control.Handle, null, IntPtr.Zero, NativeMethods.RDW_FRAME); + } + } + + /// + /// Called when our code loads. Here we connect us as the selection UI handler for ourselves. This is a special case because for the top level document, we are our own selection UI handler. + /// + private void OnLoadComplete(object source, EventArgs evevent) + { + if (Control is Form form) + { + // The form's ClientSize is reported including the ScrollBar's height. We need to account for this in order to display the form with scrollbars correctly. + int clientWidth = form.ClientSize.Width; + int clientHeight = form.ClientSize.Height; + if (form.HorizontalScroll.Visible && form.AutoScroll) + { + clientHeight += SystemInformation.HorizontalScrollBarHeight; + } + if (form.VerticalScroll.Visible && form.AutoScroll) + { + clientWidth += SystemInformation.VerticalScrollBarWidth; + } + + // ApplyAutoScaling causes WmWindowPosChanging to be called and there we calculate if we need to compensate for a menu being visible we were causing that calculation to fail if we set ClientSize too early. we now do the right thing AND check again if we need to compensate for the menu. + ApplyAutoScaling(_autoScaleBaseSize, form); + ClientSize = new Size(clientWidth, clientHeight); + BehaviorService svc = (BehaviorService)GetService(typeof(BehaviorService)); + if (svc != null) + { + svc.SyncSelection(); + } + + // if there is a menu and we need to update our height because of it, do it now. + if (_heightDelta == 0) + { + _heightDelta = GetMenuHeight(); + } + + if (_heightDelta != 0) + { + form.Height += _heightDelta; + _heightDelta = 0; + } + + // After loading the form if the ControlBox and ShowInTaskbar properties are false, the form will be sized incorrectly. This is due to the text property being set after the ControlBox and ShowInTaskbar properties, which causes windows to recalculate our client area wrong. The reason it does this is because after setting the ShowInTaskbar and ControlBox it assumes we have no titlebar, and bases the clientSize we pass it on that. In reality our ClientSize DOES depend on having a titlebar, so windows gets confused. This only happens at designtime, because at runtime our special DesignTime only MainMenu is not around to mess things up. Because of this, I'm adding this nasty workaround to correctly update the height at design time. + if (!form.ControlBox && !form.ShowInTaskbar && !string.IsNullOrEmpty(form.Text) && Menu != null && !IsMenuInherited) + { + form.Height += SystemInformation.CaptionHeight + 1; + } + form.PerformLayout(); + } + + } + + /// + /// Allows a designer to filter the set of properties the component it is designing will expose through the TypeDescriptor object. This method is called immediately before its corresponding "Post" method. If you are overriding this method you should call the base implementation before you perform your own filtering. + /// + protected override void PreFilterProperties(IDictionary properties) + { + PropertyDescriptor prop; + base.PreFilterProperties(properties); + // Handle shadowed properties + string[] shadowProps = new string[] { "Opacity", "Menu", "IsMdiContainer", "Size", "ShowInTaskBar", "WindowState", "AutoSize", "AcceptButton", "CancelButton" }; + Attribute[] empty = new Attribute[0]; + for (int i = 0; i < shadowProps.Length; i++) + { + prop = (PropertyDescriptor)properties[shadowProps[i]]; + if (prop != null) + { + properties[shadowProps[i]] = TypeDescriptor.CreateProperty(typeof(FormDocumentDesigner), prop, empty); + } + } + + // Mark auto scale base size as serializable again so we can monitor it for backwards compat. + prop = (PropertyDescriptor)properties["AutoScaleBaseSize"]; + if (prop != null) + { + properties["AutoScaleBaseSize"] = TypeDescriptor.CreateProperty(typeof(FormDocumentDesigner), prop, DesignerSerializationVisibilityAttribute.Visible); + } + + // And set the new default value attribute for client base size, and shadow it as well. + prop = (PropertyDescriptor)properties["ClientSize"]; + if (prop != null) + { + properties["ClientSize"] = TypeDescriptor.CreateProperty(typeof(FormDocumentDesigner), prop, new DefaultValueAttribute(new Size(-1, -1))); + } + } + + /// + /// Handles the WM_WINDOWPOSCHANGING message + /// + private unsafe void WmWindowPosChanging(ref Message m) + { + NativeMethods.WINDOWPOS* wp = (NativeMethods.WINDOWPOS*)m.LParam; + bool updateSize = _inAutoscale; + if (!updateSize) + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null) + { + updateSize = host.Loading; + } + } + // we want to update the size if we have a menu and... + // 1) we're doing an autoscale + // 2) we're loading a form without an inherited menu (inherited forms will already have the right size) + if (updateSize && Menu != null && (wp->flags & NativeMethods.SWP_NOSIZE) == 0 && (IsMenuInherited || _inAutoscale)) + { + _heightDelta = GetMenuHeight(); + } + } + + /// + /// Overrides our base class WndProc to provide support for the menu editor service. + /// + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_WINDOWPOSCHANGING: + WmWindowPosChanging(ref m); + break; + } + base.WndProc(ref m); + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/GroupedContextMenuStrip.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/GroupedContextMenuStrip.cs new file mode 100644 index 00000000000..34e0403dc5e --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/GroupedContextMenuStrip.cs @@ -0,0 +1,90 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; + +namespace System.Windows.Forms.Design +{ + internal class GroupedContextMenuStrip : ContextMenuStrip + { + private StringCollection _groupOrdering; + private ContextMenuStripGroupCollection _groups; + private bool _populated = false; + + public bool Populated + { + set => _populated = value; + } + + public GroupedContextMenuStrip() + { + } + + public ContextMenuStripGroupCollection Groups + { + get + { + if (_groups == null) + { + _groups = new ContextMenuStripGroupCollection(); + } + return _groups; + } + } + public StringCollection GroupOrdering + { + get + { + if (_groupOrdering == null) + { + _groupOrdering = new StringCollection(); + } + return _groupOrdering; + } + } + + // merges all the items which are currently in the groups into the items collection. + public void Populate() + { + Items.Clear(); + foreach (string groupName in GroupOrdering) + { + if (_groups.ContainsKey(groupName)) + { + List items = _groups[groupName].Items; + + if (Items.Count > 0 && items.Count > 0) + { + Items.Add(new ToolStripSeparator()); + } + foreach (ToolStripItem item in items) + { + Items.Add(item); + } + } + } + _populated = true; + } + + protected override void OnOpening(CancelEventArgs e) + { + SuspendLayout(); + if (!_populated) + { + Populate(); + } + RefreshItems(); + ResumeLayout(true); + PerformLayout(); + e.Cancel = (Items.Count == 0); + base.OnOpening(e); + } + + public virtual void RefreshItems() + { + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IContainsThemedScrollbarWindows.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IContainsThemedScrollbarWindows.cs new file mode 100644 index 00000000000..cc14a163630 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IContainsThemedScrollbarWindows.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; + +namespace System.Windows.Forms.Design +{ + public enum ThemedScrollbarMode + { + /// + /// The window and all of its children will have themed scrollbars + /// + All = 1, + + /// + /// The window and all of its children will be un-themed + /// + None = 2, + + /// + /// The window will have themed scrollbars but all of its children will be un-themed + /// + OnlyTopLevel = 3 + }; + + public struct ThemedScrollbarWindow + { + public IntPtr Handle; + public ThemedScrollbarMode Mode; + }; + + /// + /// Returns an enumeration of windows and flags of how their scrollbars need to be themed when the designer is running inside Visual Studio. + /// + public interface IContainsThemedScrollbarWindows + { + IEnumerable ThemedScrollbarWindows(); + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IMenuStatusHandler.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IMenuStatusHandler.cs new file mode 100644 index 00000000000..b472ec881a1 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IMenuStatusHandler.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel.Design; + +namespace System.Windows.Forms.Design +{ + /// + /// We plug this interface into the designer event service for overriding menu commands. + /// + internal interface IMenuStatusHandler + { + /// + /// CommandSet will check with this handler on each status update to see if the handler wants to override the availability of this command. + /// + bool OverrideInvoke(MenuCommand cmd); + + /// + /// CommandSet will check with this handler on each status update to see if the handler wants to override the availability of this command. + /// + bool OverrideStatus(MenuCommand cmd); + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IMouseHandler.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IMouseHandler.cs new file mode 100644 index 00000000000..720af4fb184 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IMouseHandler.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; + +namespace System.Windows.Forms.Design +{ + internal interface IMouseHandler + { + /// + /// This is called when the user double clicks on a component. The typical behavior is to create an event handler for the component's default event and navigate to the handler. + /// + void OnMouseDoubleClick(IComponent component); + + /// + /// This is called when a mouse button is depressed. This will perform the default drag action for the selected components, which is to move those components around by the mouse. + /// + void OnMouseDown(IComponent component, MouseButtons button, int x, int y); + + /// + /// This is called when the mouse momentarially hovers over the view for the given component. + /// + void OnMouseHover(IComponent component); + + /// + /// This is called for each movement of the mouse. + /// + void OnMouseMove(IComponent component, int x, int y); + /// + /// This is called when the user releases the mouse from a component. This will update the UI to reflect the release of the mouse. + /// + void OnMouseUp(IComponent component, MouseButtons button); + + /// + /// This is called when the cursor for the given component should be updated. The mouse is always over the given component's view when this is called. + /// + void OnSetCursor(IComponent component); + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IOverlayService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IOverlayService.cs new file mode 100644 index 00000000000..0bc202dca49 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IOverlayService.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; + +namespace System.Windows.Forms.Design +{ + /// + /// IOverlayService is a service that supports adding simple overlay windows to a design surface. Overlay windows can be used to paint extra glyphs on top of existing controls. Once an overlay is added, it will be forced on top of the Z-order for the other controls and overlays. If you want the overlay to be transparent, then you must do this work yourself. A typical way to make an overlay control transparent is to use the method setRegion on the control class to define the non-transparent portion of the control. + /// + internal interface IOverlayService + { + /// + /// Pushes the given control on top of the overlay list. This is a "push" operation, meaning that it forces this control to the top of the existing overlay list. + /// + int PushOverlay(Control control); + + /// + /// Removes the given control from the overlay list. Unlike pushOverlay, this can remove a control from the middle of the overlay list. + /// + void RemoveOverlay(Control control); + + /// + /// Inserts the given control from the overlay list. You need to pass the index of the overlay. + /// + void InsertOverlay(Control control, int index); + + /// + /// Invalidates the overlays + /// + void InvalidateOverlays(Rectangle screenRectangle); + + /// + /// Invalidates the overlays + /// + void InvalidateOverlays(Region screenRegion); + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ISelectionUIService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ISelectionUIService.cs new file mode 100644 index 00000000000..a07bb34a64c --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ISelectionUIService.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; + +namespace System.Windows.Forms.Design +{ + /// + /// The selection UI service is used to provide a standard user interface for selection across designers. Using this service is optional, but is recommended to provide a standard UI component selection. + /// + internal interface ISelectionUIService + { + /// + /// Determines if the selection UI is shown or not. + /// + bool Visible { get; set; } + + /// + /// Adds an event handler to the ContainerSelectorActive event. This event is fired whenever the user interacts with the container selector in a manor that would indicate that the selector should continued to be displayed. Since the container selector normally will vanish after a timeout, designers should listen to this event and reset the timeout when this event occurs. + /// + event ContainerSelectorActiveEventHandler ContainerSelectorActive; + + /// + /// Assigns a selection UI handler to a given component. The handler will be called when the UI service needs information about the component. A single selection UI handler can be assigned to multiple components. When multiple components are dragged, only a single handler may control the drag. Because of this, only components that are assigned the same handler as the primary selection are included in drag operations. A selection UI handler is automatically unassigned when the component is removed from the container or disposed. + /// + void AssignSelectionUIHandler(object component, ISelectionUIHandler handler); + + void ClearSelectionUIHandler(object component, ISelectionUIHandler handler); + + /// + /// This can be called by an outside party to begin a drag of the currently selected set of components. At least one designer must have added a UI handler or else this method will always return false. + /// + bool BeginDrag(SelectionRules rules, int initialX, int initialY); + + /// + /// This can be used to determine if the user is in the middle of a drag operation. + /// + bool Dragging { get; } + + /// + /// Called by an outside party to update drag information. This can only be called after a successful call to beginDrag. + /// + void DragMoved(Rectangle offset); + + /// + /// Called by an outside party to finish a drag operation. This can only be called after a successful call to beginDrag. + /// + void EndDrag(bool cancel); + + /// + /// Filters the set of selected components. The selection service will retrieve all components that are currently selected. This method allows you to filter this set down to components that match your criteria. The selectionRules parameter must contain one or more flags from the SelectionRules class. These flags allow you to constrain the set of selected objects to visible, movable, sizeable or all objects. + /// + object[] FilterSelection(object[] components, SelectionRules selectionRules); + + /// + /// Retrieves the width and height of a selection border grab handle. Designers may need this to properly position their user interfaces. + /// + Size GetAdornmentDimensions(AdornmentType adornmentType); + + /// + /// Tests to determine if the given screen coordinate is over an adornment for the specified component. This will only return true if the adornment, and selection UI, is visible. + /// + bool GetAdornmentHitTest(object component, Point pt); + + /// + /// Gets a value indicating whether the specified component is the currently selected container. + /// + bool GetContainerSelected(object component); + + /// + /// Retrieves a set of flags that define rules for the selection. Selection rules indicate if the given component can be moved or sized, for example. + /// + SelectionRules GetSelectionRules(object component); + + /// + /// Allows you to configure the style of the selection frame that a component uses. This is useful if your component supports different modes of operation (such as an in-place editing mode and a static design mode). Where possible, you should leave the selection style as is and use the design-time hit testing feature of the IDesigner interface to provide features at design time. The value of style must be one of the SelectionStyle enum values. The selection style is only valid for the duration that the component is selected. + /// + SelectionStyles GetSelectionStyle(object component); + + /// + /// Changes the container selection status of the specified component. + /// + void SetContainerSelected(object component, bool selected); + + /// + /// Allows you to configure the style of the selection frame that a component uses. This is useful if your component supports different modes of operation (such as an in-place editing mode and a static design mode). Where possible, you should leave the selection style as is and use the design-time hit testing feature of the IDesigner interface to provide features at design time. The value of style must be one of the SelectionStyle enum values. The selection style is only valid for the duration that the component is selected. + /// + void SetSelectionStyle(object component, SelectionStyles style); + + /// + /// This should be called when a component has been moved, sized or re-parented, but the change was not the result of a property change. All property changes are monitored by the selection UI service, so this is automatic most of the time. There are times, however, when a component may be moved without a property change notification occurring. Scrolling an auto scroll Win32 form is an example of this. This method simply re-queries all currently selected components for their bounds and udpates the selection handles for any that have changed. + /// + void SyncSelection(); + + /// + /// This should be called when a component's property changed, that the designer thinks should result in a selection UI change. This method simply re-queries all currently selected components for their bounds and udpates the selection handles for any that have changed. + /// + void SyncComponent(object component); + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ISplitWindowService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ISplitWindowService.cs new file mode 100644 index 00000000000..5a072d29527 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ISplitWindowService.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Design +{ + /// + /// Supports the hosting of several 'pane' windows separated by splitter bars. + /// + internal interface ISplitWindowService + { + /// + /// Requests the service to add a window 'pane'. + /// + void AddSplitWindow(Control window); + + /// + /// Requests the service to remove a window 'pane'. + /// + void RemoveSplitWindow(Control window); + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ISupportInSituService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ISupportInSituService.cs new file mode 100644 index 00000000000..687f296de27 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ISupportInSituService.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Design +{ + /// + /// Provides an interface for a designer to support Insitu editing for selected components. + /// + [Runtime.InteropServices.ComVisible(true)] + internal interface ISupportInSituService + { + /// + /// Returns if the service is interested in InSitu Edit on Key Messages.. + /// + bool IgnoreMessages { get; } + + /// + /// This method allows the service to handle the first WM_CHAR message. The implementer for this service can perform any tasks that it wants when it gets this message. + /// e.g : ToolStripInSituService shows the Editor for each ToolStripItem in HandleKeyChar() + /// + void HandleKeyChar(); + + /// + /// Returns the Window Handle that gets all the Keyboarf messages once in InSitu. + /// + IntPtr GetEditWindow(); + + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/InheritanceUI.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/InheritanceUI.cs new file mode 100644 index 00000000000..e1ccf59be38 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/InheritanceUI.cs @@ -0,0 +1,119 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; + +namespace System.Windows.Forms.Design +{ + /// + /// This class handles the user interface for inherited components. + /// + internal class InheritanceUI + { + private static Bitmap s_inheritanceGlyph; + private static Rectangle s_inheritanceGlyphRect; + private ToolTip _tooltip; + + /// + /// The bitmap we use to show inheritance. + /// + public Bitmap InheritanceGlyph + { + get + { + if (s_inheritanceGlyph == null) + { + s_inheritanceGlyph = new Bitmap(typeof(InheritanceUI), "InheritedGlyph.bmp"); + s_inheritanceGlyph.MakeTransparent(); + if (DpiHelper.IsScalingRequired) + { + DpiHelper.ScaleBitmapLogicalToDevice(ref s_inheritanceGlyph); + } + } + return s_inheritanceGlyph; + } + } + + /// + /// The rectangle surrounding the glyph. + /// + public Rectangle InheritanceGlyphRectangle + { + get + { + if (s_inheritanceGlyphRect == Rectangle.Empty) + { + Size size = InheritanceGlyph.Size; + s_inheritanceGlyphRect = new Rectangle(0, 0, size.Width, size.Height); + } + return s_inheritanceGlyphRect; + } + } + + /// + /// Adds an inherited control to our list. This creates a tool tip for that control. + /// + public void AddInheritedControl(Control c, InheritanceLevel level) + { + if (_tooltip == null) + { + _tooltip = new ToolTip + { + ShowAlways = true + }; + } + + Debug.Assert(level != InheritanceLevel.NotInherited, "This should only be called for inherited components."); + string text; + if (level == InheritanceLevel.InheritedReadOnly) + { + text = SR.DesignerInheritedReadOnly; + } + else + { + text = SR.DesignerInherited; + } + + _tooltip.SetToolTip(c, text); + + // Also, set all of its non-sited children + foreach (Control child in c.Controls) + { + if (child.Site == null) + { + _tooltip.SetToolTip(child, text); + } + } + } + + public void Dispose() + { + if (_tooltip != null) + { + _tooltip.Dispose(); + } + } + + /// + /// Removes a previously added inherited control. + /// + public void RemoveInheritedControl(Control c) + { + if (_tooltip != null && _tooltip.GetToolTip(c).Length > 0) + { + _tooltip.SetToolTip(c, null); + // Also, set all of its non-sited children + foreach (Control child in c.Controls) + { + if (child.Site == null) + { + _tooltip.SetToolTip(child, null); + } + } + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ItemTypeToolStripMenuItem.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ItemTypeToolStripMenuItem.cs new file mode 100644 index 00000000000..46bc91ee3f0 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ItemTypeToolStripMenuItem.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; +using System.Drawing.Design; + +namespace System.Windows.Forms.Design +{ + /// + /// Associates Type with ToolStripMenuItem. + /// + internal class ItemTypeToolStripMenuItem : ToolStripMenuItem + { + private static readonly string s_systemWindowsFormsNamespace = typeof(ToolStripItem).Namespace; + private static readonly ToolboxItem s_invalidToolboxItem = new ToolboxItem(); + private readonly Type _itemType; + private bool _convertTo = false; + private ToolboxItem _tbxItem = s_invalidToolboxItem; + private Image _image = null; + + public ItemTypeToolStripMenuItem(Type t) => _itemType = t; + + public static string SystemWindowsFormsNamespace => s_systemWindowsFormsNamespace; + + public Type ItemType + { + get => _itemType; + } + + public bool ConvertTo + { + get => _convertTo; + set => _convertTo = value; + } + + public override Image Image + { + get + { + if (_image == null) + { + _image = ToolStripDesignerUtils.GetToolboxBitmap(ItemType); + } + return _image; + } + set + { + } + } + + public override string Text + { + get => ToolStripDesignerUtils.GetToolboxDescription(ItemType); + set + { + } + } + + public ToolboxItem TbxItem { get => _tbxItem; set => _tbxItem = value; } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + TbxItem = null; + } + base.Dispose(disposing); + } + + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/NewItemsContextMenuStrip.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/NewItemsContextMenuStrip.cs new file mode 100644 index 00000000000..7d0d5b4a437 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/NewItemsContextMenuStrip.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; + +namespace System.Windows.Forms.Design +{ + internal class NewItemsContextMenuStrip : GroupedContextMenuStrip + { + private readonly IComponent _component = null; + private readonly EventHandler _onClick = null; + private readonly bool _convertTo = false; + private readonly IServiceProvider _serviceProvider = null; + private readonly ToolStripItem _currentItem; + + public NewItemsContextMenuStrip(IComponent component, ToolStripItem currentItem, EventHandler onClick, bool convertTo, IServiceProvider serviceProvider) + { + _component = component; + _onClick = onClick; + _convertTo = convertTo; + _serviceProvider = serviceProvider; + _currentItem = currentItem; + if (serviceProvider.GetService(typeof(IUIService)) is IUIService uis) + { + Renderer = (ToolStripProfessionalRenderer)uis.Styles["VsRenderer"]; + } + } + protected override void OnOpening(CancelEventArgs e) + { + Groups["StandardList"].Items.Clear(); + Groups["CustomList"].Items.Clear(); + Populated = false; + + // plumb through the standard and custom items. + foreach (ToolStripItem item in ToolStripDesignerUtils.GetStandardItemMenuItems(_component, _onClick, _convertTo)) + { + Groups["StandardList"].Items.Add(item); + if (_convertTo) + { + if (item is ItemTypeToolStripMenuItem toolItem && _currentItem != null && toolItem.ItemType == _currentItem.GetType()) + { + toolItem.Enabled = false; + } + } + + } + foreach (ToolStripItem item in ToolStripDesignerUtils.GetCustomItemMenuItems(_component, _onClick, _convertTo, _serviceProvider)) + { + Groups["CustomList"].Items.Add(item); + if (_convertTo) + { + if (item is ItemTypeToolStripMenuItem toolItem && _currentItem != null && toolItem.ItemType == _currentItem.GetType()) + { + toolItem.Enabled = false; + } + } + } + base.OnOpening(e); + } + + // Please refer to VsW: 505199 for more details. We dont want the runtime behavior for this Design Time only DropDown and hence we overide the ProcessDialogKey and just close the DropDown instead of running through the runtime implementation for RIGHT/LEFT Keys which ends up setting ModalMenuFilter. + protected override bool ProcessDialogKey(Keys keyData) + { + Keys keyCode = (Keys)keyData & Keys.KeyCode; + switch (keyCode) + { + case Keys.Left: + case Keys.Right: + Close(); + return true; + } + return base.ProcessDialogKey(keyData); + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ParentControlDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ParentControlDesigner.cs index f26fa746e54..3e1bd27a84b 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ParentControlDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ParentControlDesigner.cs @@ -4,6 +4,8 @@ using System.Collections; using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Design; @@ -18,6 +20,14 @@ namespace System.Windows.Forms.Design /// public class ParentControlDesigner : ControlDesigner, IOleDragClient { + private Control pendingRemoveControl; // we've gotten an OnComponentRemoving, and are waiting for OnComponentRemove + + private OleDragDropHandler oleDragDropHandler; // handler for ole drag drop operations + private IComponentChangeService componentChangeSvc; + private StatusCommandUI statusCommandUI; // UI for setting the StatusBar Information.. + + private int suspendChanging = 0; + /// /// This is called after the user selects a toolbox item (that has a ParentControlDesigner /// associated with it) and draws a reversible rectangle on a designer's surface. If @@ -92,6 +102,256 @@ protected Size GridSize /// public override IList SnapLines => throw new NotImplementedException(SR.NotImplementedByDesign); + internal Size ParentGridSize + { + get => GridSize; + } + + internal OleDragDropHandler GetOleDragHandler() + { + if (oleDragDropHandler == null) + { + oleDragDropHandler = new OleDragDropHandler(null, (IServiceProvider)GetService(typeof(IDesignerHost)), this); + } + return oleDragDropHandler; + } + + internal void AddControl(Control newChild, IDictionary defaultValues) + { + Point location = Point.Empty; + Size size = Size.Empty; + Size offset = new Size(0, 0); + bool hasLocation = (defaultValues != null && defaultValues.Contains("Location")); + bool hasSize = (defaultValues != null && defaultValues.Contains("Size")); + + if (hasLocation) + location = (Point)defaultValues["Location"]; + if (hasSize) + size = (Size)defaultValues["Size"]; + if (defaultValues != null && defaultValues.Contains("Offset")) + { + offset = (Size)defaultValues["Offset"]; + } + + // If this component doesn't have a control designer, or if this control is top level, then ignore it. We have the reverse logic in OnComponentAdded in the document designer so that we will add those guys to the tray. Also, if the child-control has already been parented, we assume it's also been located and return immediately. Otherwise, proceed with the parenting and locating. + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null && newChild != null && !Control.Contains(newChild) && (host.GetDesigner(newChild) as ControlDesigner) != null && !(newChild is Form && ((Form)newChild).TopLevel)) + { + Rectangle bounds = new Rectangle(); + // If we were provided with a location, convert it to parent control coordinates. Otherwise, get the control's size and put the location in the middle of it + if (hasLocation) + { + location = Control.PointToClient(location); + bounds.X = location.X; + bounds.Y = location.Y; + } + else + { + // is the currently selected control this container? + ISelectionService selSvc = (ISelectionService)GetService(typeof(ISelectionService)); + object primarySelection = selSvc.PrimarySelection; + Control selectedControl = null; + if (primarySelection != null) + { + selectedControl = ((IOleDragClient)this).GetControlForComponent(primarySelection); + } + + // If the resulting control that came back isn't sited, it's not part of the design surface and should not be used as a marker. + if (selectedControl != null && selectedControl.Site == null) + { + selectedControl = null; + } + + // if the currently selected container is this parent control, default to 0,0 + if (primarySelection == Component || selectedControl == null) + { + bounds.X = DefaultControlLocation.X; + bounds.Y = DefaultControlLocation.Y; + } + else + { + // otherwise offset from selected control. + bounds.X = selectedControl.Location.X + GridSize.Width; + bounds.Y = selectedControl.Location.Y + GridSize.Height; + } + + } + // If we were not given a size, ask the control for its default. We also update the location here so the control is in the middle of the user's point, rather than at the edge. + if (hasSize) + { + bounds.Width = size.Width; + bounds.Height = size.Height; + } + else + { + bounds.Size = GetDefaultSize(newChild); + } + + // If we were given neither, center the control + if (!hasSize && !hasLocation) + { + // get the adjusted location, then inflate the rect so we can find a nice spot for this control to live. + Rectangle tempBounds = GetAdjustedSnapLocation(Rectangle.Empty, bounds); + // compute the stacking location + tempBounds = GetControlStackLocation(tempBounds); + bounds = tempBounds; + } + else + { + // Finally, convert the bounds to the appropriate grid snaps + bounds = GetAdjustedSnapLocation(Rectangle.Empty, bounds); + } + + // Adjust for the offset, if any + bounds.X += offset.Width; + bounds.Y += offset.Height; + // check to see if we have additional information for bounds from the behaviorservice dragdrop logic + if (defaultValues != null && defaultValues.Contains("ToolboxSnapDragDropEventArgs")) + { + ToolboxSnapDragDropEventArgs e = defaultValues["ToolboxSnapDragDropEventArgs"] as ToolboxSnapDragDropEventArgs; + Debug.Assert(e != null, "Why can't we get a ToolboxSnapDragDropEventArgs object out of our default values?"); + Rectangle snappedBounds = DesignerUtils.GetBoundsFromToolboxSnapDragDropInfo(e, bounds, Control.IsMirrored); + //Make sure the snapped bounds intersects with the bounds of the root control before we go adjusting the drag offset. A race condition exists where the user can drag a tbx item so fast that the adorner window will never receive the proper drag/mouse move messages and never properly adjust the snap drag info. This cause the control to be added @ 0,0 w.r.t. the adorner window. + if (host.RootComponent is Control rootControl && snappedBounds.IntersectsWith(rootControl.ClientRectangle)) + { + bounds = snappedBounds; + } + } + // Parent the control to the designer and set it to the front. + PropertyDescriptor controlsProp = TypeDescriptor.GetProperties(Control)["Controls"]; + if (componentChangeSvc != null) + { + componentChangeSvc.OnComponentChanging(Control, controlsProp); + } + AddChildControl(newChild); + // Now see if the control has size and location properties. Update these values if it does. + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(newChild); + if (props != null) + { + PropertyDescriptor prop = props["Size"]; + if (prop != null) + { + prop.SetValue(newChild, new Size(bounds.Width, bounds.Height)); + } + + // ControlDesigner shadows the Location property. If the control is parented and the parent is a scrollable control, then it expects the Location to be in displayrectangle coordinates. At this point bounds are in clientrectangle coordinates, so we need to check if we need to adjust the coordinates. The reason this worked in Everett was that the AddChildControl was done AFTER this. The AddChildControl was moved above a while back. Not sure what will break if AddChildControl is moved down below, so let's just fix up things here. + Point pt = new Point(bounds.X, bounds.Y); + if (newChild.Parent is ScrollableControl p) + { + Point ptScroll = p.AutoScrollPosition; + pt.Offset(-ptScroll.X, -ptScroll.Y); //always want to add the control below/right of the AutoScrollPosition + } + + prop = props["Location"]; + if (prop != null) + { + prop.SetValue(newChild, pt); + } + } + + if (componentChangeSvc != null) + { + componentChangeSvc.OnComponentChanged(Control, controlsProp, Control.Controls, Control.Controls); + } + newChild.Update(); + } + } + + internal virtual void AddChildControl(Control newChild) + { + if (newChild.Left == 0 && newChild.Top == 0 && newChild.Width >= Control.Width && newChild.Height >= Control.Height) + { + // bump the control down one gridsize just so it's selectable... + Point loc = newChild.Location; + loc.Offset(GridSize.Width, GridSize.Height); + newChild.Location = loc; + } + Control.Controls.Add(newChild); + Control.Controls.SetChildIndex(newChild, 0); + } + + private Rectangle GetControlStackLocation(Rectangle centeredLocation) + { + Control parent = Control; + int parentHeight = parent.ClientSize.Height; + int parentWidth = parent.ClientSize.Width; + if (centeredLocation.Bottom >= parentHeight || centeredLocation.Right >= parentWidth) + { + centeredLocation.X = DefaultControlLocation.X; + centeredLocation.Y = DefaultControlLocation.Y; + } + return centeredLocation; + } + + [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] + private Size GetDefaultSize(IComponent component) + { + //Check to see if the control is AutoSized. VSWhidbey #416721 + PropertyDescriptor prop = TypeDescriptor.GetProperties(component)["AutoSize"]; + Size size; + if (prop != null && + !(prop.Attributes.Contains(DesignerSerializationVisibilityAttribute.Hidden) || + prop.Attributes.Contains(BrowsableAttribute.No))) + { + bool autoSize = (bool)prop.GetValue(component); + if (autoSize) + { + prop = TypeDescriptor.GetProperties(component)["PreferredSize"]; + if (prop != null) + { + size = (Size)prop.GetValue(component); + if (size != Size.Empty) + { + return size; + } + } + } + } + + // attempt to get the size property of our component + prop = TypeDescriptor.GetProperties(component)["Size"]; + if (prop != null) + { + // first, let's see if we can get a valid size... + size = (Size)prop.GetValue(component); + // ...if not, we'll see if there's a default size attribute... + if (size.Width <= 0 || size.Height <= 0) + { + DefaultValueAttribute sizeAttr = (DefaultValueAttribute)prop.Attributes[typeof(DefaultValueAttribute)]; + if (sizeAttr != null) + { + return ((Size)sizeAttr.Value); + } + } + else + { + return size; + } + } + // Couldn't get the size or a def size attrib, returning 75,23... + return (new Size(75, 23)); + } + + private Rectangle GetAdjustedSnapLocation(Rectangle originalRect, Rectangle dragRect) + { + Rectangle adjustedRect = GetUpdatedRect(originalRect, dragRect, true); + //now, preserve the width and height that was originally passed in + adjustedRect.Width = dragRect.Width; + adjustedRect.Height = dragRect.Height; + // we need to keep in mind that if we adjust to the snap, that we could have possibly moved the control's position outside of the display rect. ex: groupbox's display rect.x = 3, but we might snap to 0. so we need to check with the control's designer to make sure this doesn't happen + Point minimumLocation = DefaultControlLocation; + if (adjustedRect.X < minimumLocation.X) + { + adjustedRect.X = minimumLocation.X; + } + if (adjustedRect.Y < minimumLocation.Y) + { + adjustedRect.Y = minimumLocation.Y; + } + // here's our rect that has been snapped to grid + return adjustedRect; + } + bool IOleDragClient.CanModifyComponents => throw new NotImplementedException(); bool IOleDragClient.AddComponent(IComponent component, string name, bool firstAdd) @@ -246,6 +506,12 @@ public override GlyphCollection GetGlyphs(GlyphSelectionType selectionType) throw new NotImplementedException(SR.NotImplementedByDesign); } + internal Point GetSnappedPoint(Point pt) + { + Rectangle r = GetUpdatedRect(Rectangle.Empty, new Rectangle(pt.X, pt.Y, 0, 0), false); + return new Point(r.X, r.Y); + } + /// /// Updates the given rectangle, adjusting it for grid snaps as /// needed. @@ -261,7 +527,64 @@ protected Rectangle GetUpdatedRect(Rectangle originalRect, Rectangle dragRect, b /// public override void Initialize(IComponent component) { - throw new NotImplementedException(SR.NotImplementedByDesign); + base.Initialize(component); + if (Control is ScrollableControl) + { + ((ScrollableControl)Control).Scroll += new ScrollEventHandler(OnScroll); + } + EnableDragDrop(true); + // Hook load events. At the end of load, we need to do a scan through all of our child controls to see which ones are being inherited. We connect these up. + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null) + { + componentChangeSvc = (IComponentChangeService)host.GetService(typeof(IComponentChangeService)); + if (componentChangeSvc != null) + { + componentChangeSvc.ComponentRemoving += new ComponentEventHandler(OnComponentRemoving); + componentChangeSvc.ComponentRemoved += new ComponentEventHandler(OnComponentRemoved); + } + } + // update the Status Command + statusCommandUI = new StatusCommandUI(component.Site); + } + + private void OnComponentRemoved(object sender, ComponentEventArgs e) + { + if (e.Component == pendingRemoveControl) + { + pendingRemoveControl = null; + componentChangeSvc.OnComponentChanged(Control, TypeDescriptor.GetProperties(Control)["Controls"], null, null); + } + } + + private void OnComponentRemoving(object sender, ComponentEventArgs e) + { + if (e.Component is Control comp && comp.Parent != null && comp.Parent == Control) + { + pendingRemoveControl = (Control)comp; + //We suspend Component Changing Events for bulk operations to avoid unnecessary serialization\deserialization for undo + if (suspendChanging == 0) + { + componentChangeSvc.OnComponentChanging(Control, TypeDescriptor.GetProperties(Control)["Controls"]); + } + } + } + + internal void SuspendChangingEvents() + { + suspendChanging++; + Debug.Assert(suspendChanging > 0, "Unbalanced SuspendChangingEvents\\ResumeChangingEvents"); + } + + internal void ForceComponentChanging() + { + componentChangeSvc.OnComponentChanging(Control, TypeDescriptor.GetProperties(Control)["Controls"]); + } + + internal void ResumeChangingEvents() + { + suspendChanging--; + Debug.Assert(suspendChanging >= 0, "Unbalanced SuspendChangingEvents\\ResumeChangingEvents"); } /// diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionManager.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionManager.cs new file mode 100644 index 00000000000..76d211816a2 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionManager.cs @@ -0,0 +1,478 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Drawing; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// The SelectionBehavior is pushed onto the BehaviorStack in response to apositively hit tested SelectionGlyph. The SelectionBehavior performs two main tasks: 1) forward messages to the related ControlDesigner, and 2) calls upon the SelectionManager to push a potention DragBehavior. + internal sealed class SelectionManager : IDisposable + { + private Adorner _selectionAdorner;//used to provide all selection glyphs + private Adorner _bodyAdorner;//used to track all body glyphs for each control + private BehaviorService _behaviorService;//ptr back to our BehaviorService + private IServiceProvider _serviceProvider;//standard service provider + private readonly Hashtable _componentToDesigner;//used for quick look up of designers related to comps + private readonly Control _rootComponent;//root component being designed + private ISelectionService _selSvc;//we cache the selection service for perf. + private IDesignerHost _designerHost;//we cache the designerhost for perf. + private bool _needRefresh; // do we need to refresh? + private Rectangle[] _prevSelectionBounds;//used to only repaint the changing part of the selection + private object _prevPrimarySelection; //used to check if the primary selection changed + private Rectangle[] _curSelectionBounds; + private int _curCompIndex; + private DesignerActionUI _designerActionUI = null; // the "container" for all things related to the designer action (smartags) UI + private bool _selectionChanging; //we dont want the OnSelectionChanged to be recursively called. + + /// + /// Constructor. Here we query for necessary services and cache them for perf. reasons. We also hook to Component Added/Removed/Changed notifications so we can keep in sync when the designers' components change. Also, we create our custom Adorner and add it to the BehaviorService. + /// + public SelectionManager(IServiceProvider serviceProvider, BehaviorService behaviorService) + { + _prevSelectionBounds = null; + _prevPrimarySelection = null; + _behaviorService = behaviorService; + _serviceProvider = serviceProvider; + _selSvc = (ISelectionService)serviceProvider.GetService(typeof(ISelectionService)); + _designerHost = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost)); + if (_designerHost == null || _selSvc == null) + { + Debug.Fail("SelectionManager - Host or SelSvc is null, can't continue"); + } + + //sync the BehaviorService's begindrag event + behaviorService.BeginDrag += new BehaviorDragDropEventHandler(OnBeginDrag); + //sync the BehaviorService's Synchronize event + behaviorService.Synchronize += new EventHandler(OnSynchronize); + _selSvc.SelectionChanged += new EventHandler(OnSelectionChanged); + _rootComponent = (Control)_designerHost.RootComponent; + + //create and add both of our adorners, one for selection, one for bodies + _selectionAdorner = new Adorner(); + _bodyAdorner = new Adorner(); + behaviorService.Adorners.Add(_bodyAdorner); + behaviorService.Adorners.Add(_selectionAdorner);//adding this will cause the adorner to get setup with a ptr to the beh.svc. + _componentToDesigner = new Hashtable(); + IComponentChangeService cs = (IComponentChangeService)serviceProvider.GetService(typeof(IComponentChangeService)); + if (cs != null) + { + cs.ComponentAdded += new ComponentEventHandler(OnComponentAdded); + cs.ComponentRemoved += new ComponentEventHandler(OnComponentRemoved); + cs.ComponentChanged += new ComponentChangedEventHandler(OnComponentChanged); + } + + _designerHost.TransactionClosed += new DesignerTransactionCloseEventHandler(OnTransactionClosed); + // designeraction UI + if (_designerHost.GetService(typeof(DesignerOptionService)) is DesignerOptionService options) + { + PropertyDescriptor p = options.Options.Properties["UseSmartTags"]; + if (p != null && p.PropertyType == typeof(bool) && (bool)p.GetValue(null)) + { + _designerActionUI = new DesignerActionUI(serviceProvider, _selectionAdorner); + behaviorService.DesignerActionUI = _designerActionUI; + } + } + } + + /// + /// Returns the Adorner that contains all the BodyGlyphs for the current selection state. + /// + internal Adorner BodyGlyphAdorner + { + get => _bodyAdorner; + } + + /// + /// There are certain cases like Adding Item to ToolStrips through InSitu Editor, where there is ParentTransaction that has to be cancelled depending upon the user action. When this parent transaction is cancelled, there may be no reason to REFRESH the selectionManager which actually clears all the glyphs and readds them. This REFRESH causes a lot of flicker and can be avoided by setting this property to false. Since this property is checked in the TransactionClosed, the SelectionManager won't REFRESH and hence just eat up the refresh thus avoiding unnecessary flicker. + /// + internal bool NeedRefresh + { + get => _needRefresh; + set => _needRefresh = value; + } + + /// + /// Returns the Adorner that contains all the BodyGlyphs for the current selection state. + /// + internal Adorner SelectionGlyphAdorner + { + get => _selectionAdorner; + } + + /// + /// This method fist calls the recursive AddControlGlyphs() method. When finished, we add the final glyph(s) to the root comp. + /// + private void AddAllControlGlyphs(Control parent, ArrayList selComps, object primarySelection) + { + foreach (Control control in parent.Controls) + { + AddAllControlGlyphs(control, selComps, primarySelection); + } + + GlyphSelectionType selType = GlyphSelectionType.NotSelected; + if (selComps.Contains(parent)) + { + if (parent.Equals(primarySelection)) + { + selType = GlyphSelectionType.SelectedPrimary; + } + else + { + selType = GlyphSelectionType.Selected; + } + } + AddControlGlyphs(parent, selType); + } + + /// + /// Recursive method that goes through and adds all the glyphs of every child to our global Adorner. + /// + private void AddControlGlyphs(Control c, GlyphSelectionType selType) + { + + ControlDesigner cd = (ControlDesigner)_componentToDesigner[c]; + if (cd != null) + { + ControlBodyGlyph bodyGlyph = cd.GetControlGlyphInternal(selType); + if (bodyGlyph != null) + { + _bodyAdorner.Glyphs.Add(bodyGlyph); + if (selType == GlyphSelectionType.SelectedPrimary || + selType == GlyphSelectionType.Selected) + { + + if (_curSelectionBounds[_curCompIndex] == Rectangle.Empty) + { + _curSelectionBounds[_curCompIndex] = bodyGlyph.Bounds; + } + else + { + _curSelectionBounds[_curCompIndex] = Rectangle.Union(_curSelectionBounds[_curCompIndex], bodyGlyph.Bounds); + } + } + } + GlyphCollection glyphs = cd.GetGlyphs(selType); + if (glyphs != null) + { + _selectionAdorner.Glyphs.AddRange(glyphs); + if (selType == GlyphSelectionType.SelectedPrimary || + selType == GlyphSelectionType.Selected) + { + foreach (Glyph glyph in glyphs) + { + _curSelectionBounds[_curCompIndex] = Rectangle.Union(_curSelectionBounds[_curCompIndex], glyph.Bounds); + } + } + } + } + + if (selType == GlyphSelectionType.SelectedPrimary || selType == GlyphSelectionType.Selected) + { + _curCompIndex++; + } + } + + /// + /// Unhook all of our event notifications, clear our adorner and remove it from the Beh.Svc. + /// + // We don't need to Dispose rootComponent. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] + public void Dispose() + { + if (_designerHost != null) + { + _designerHost.TransactionClosed -= new DesignerTransactionCloseEventHandler(OnTransactionClosed); + _designerHost = null; + } + if (_serviceProvider != null) + { + IComponentChangeService cs = (IComponentChangeService)_serviceProvider.GetService(typeof(IComponentChangeService)); + if (cs != null) + { + cs.ComponentAdded -= new ComponentEventHandler(OnComponentAdded); + cs.ComponentChanged -= new ComponentChangedEventHandler(OnComponentChanged); + cs.ComponentRemoved -= new ComponentEventHandler(OnComponentRemoved); + } + if (_selSvc != null) + { + _selSvc.SelectionChanged -= new EventHandler(OnSelectionChanged); + _selSvc = null; + } + _serviceProvider = null; + } + if (_behaviorService != null) + { + _behaviorService.Adorners.Remove(_bodyAdorner); + _behaviorService.Adorners.Remove(_selectionAdorner); + _behaviorService.BeginDrag -= new BehaviorDragDropEventHandler(OnBeginDrag); + _behaviorService.Synchronize -= new EventHandler(OnSynchronize); + _behaviorService = null; + } + if (_selectionAdorner != null) + { + _selectionAdorner.Glyphs.Clear(); + _selectionAdorner = null; + } + if (_bodyAdorner != null) + { + _bodyAdorner.Glyphs.Clear(); + _bodyAdorner = null; + } + if (_designerActionUI != null) + { + _designerActionUI.Dispose(); + _designerActionUI = null; + } + } + + /// + /// Refreshes all selection Glyphs. + /// + public void Refresh() + { + NeedRefresh = false; + OnSelectionChanged(this, null); + } + + /// + /// When a component is added, we get the designer and add it to our hashtable for quick lookup. + /// + private void OnComponentAdded(object source, ComponentEventArgs ce) + { + IComponent component = ce.Component; + IDesigner designer = _designerHost.GetDesigner(component); + if (designer is ControlDesigner) + { + _componentToDesigner.Add(component, designer); + } + } + + /// + /// Before a drag, remove all glyphs that are involved in the drag operation and any that don't allow drops. + /// + private void OnBeginDrag(object source, BehaviorDragDropEventArgs e) + { + ArrayList dragComps = new ArrayList(e.DragComponents); + ArrayList glyphsToRemove = new ArrayList(); + foreach (ControlBodyGlyph g in _bodyAdorner.Glyphs) + { + if (g.RelatedComponent is Control) + { + if (dragComps.Contains(g.RelatedComponent) || + !((Control)g.RelatedComponent).AllowDrop) + { + glyphsToRemove.Add(g); + } + } + } + foreach (Glyph g in glyphsToRemove) + { + _bodyAdorner.Glyphs.Remove(g); + } + } + + // Called by the DropSourceBehavior when dragging into a new host + internal void OnBeginDrag(BehaviorDragDropEventArgs e) + { + OnBeginDrag(null, e); + } + + /// + /// When a component is changed - we need to refresh the selection. + /// + private void OnComponentChanged(object source, ComponentChangedEventArgs ce) + { + if (_selSvc.GetComponentSelected(ce.Component)) + { + if (!_designerHost.InTransaction) + { + Refresh(); + } + else + { + NeedRefresh = true; + } + } + } + + /// + /// When a component is removed - we remove the key & value from our hashtable. + /// + private void OnComponentRemoved(object source, ComponentEventArgs ce) + { + if (_componentToDesigner.Contains(ce.Component)) + { + _componentToDesigner.Remove(ce.Component); + } + //remove the associated designeractionpanel + if (_designerActionUI != null) + { + _designerActionUI.RemoveActionGlyph(ce.Component); + } + } + /// + /// Computes the region representing the difference between the old selection and the new selection. + /// + private Region DetermineRegionToRefresh(object primarySelection) + { + Region toRefresh = new Region(Rectangle.Empty); + Rectangle[] larger; + Rectangle[] smaller; + if (_curSelectionBounds.Length >= _prevSelectionBounds.Length) + { + larger = _curSelectionBounds; + smaller = _prevSelectionBounds; + } + else + { + larger = _prevSelectionBounds; + smaller = _curSelectionBounds; + } + + // we need to make sure all of the rects in the smaller array are accounted for. Any that don't intersect a rect in the larger array need to be included in the region to repaint. + bool[] intersected = new bool[smaller.Length]; + for (int i = 0; i < smaller.Length; i++) + { + intersected[i] = false; + } + + // determine which rects in the larger array need to be included in the region to invalidate by intersecting with rects in the smaller array. + for (int l = 0; l < larger.Length; l++) + { + bool largeIntersected = false; + Rectangle large = larger[l]; + for (int s = 0; s < smaller.Length; s++) + { + if (large.IntersectsWith(smaller[s])) + { + Rectangle small = smaller[s]; + largeIntersected = true; + if (large != small) + { + toRefresh.Union(large); + toRefresh.Union(small); + } + intersected[s] = true; + break; + } + } + if (!largeIntersected) + { + toRefresh.Union(large); + } + } + + // now add any rects from the smaller array that weren't accounted for + for (int k = 0; k < intersected.Length; k++) + { + if (!intersected[k]) + { + toRefresh.Union(smaller[k]); + } + } + + using (Graphics g = _behaviorService.AdornerWindowGraphics) + { + //If all that changed was the primary selection, then the refresh region was empty, but we do need to update the 2 controls. VSWhidbey #269806 + if (toRefresh.IsEmpty(g) && primarySelection != null && !primarySelection.Equals(_prevPrimarySelection)) + { + for (int i = 0; i < _curSelectionBounds.Length; i++) + { + toRefresh.Union(_curSelectionBounds[i]); + } + } + } + return toRefresh; + } + + /// + /// Event handler for the behaviorService's Synchronize event + /// + private void OnSynchronize(object sender, EventArgs e) + { + Refresh(); + } + + /// + /// On every selectionchange, we remove all glyphs, get the newly selected components, and re-add all glyphs back to the Adorner. + /// + private void OnSelectionChanged(object sender, EventArgs e) + { + // Note: selectionChanging would guard against a re-entrant code... Since we dont want to be in messed up state when adding new Glyphs. + if (!_selectionChanging) + { + _selectionChanging = true; + _selectionAdorner.Glyphs.Clear(); + _bodyAdorner.Glyphs.Clear(); + ArrayList selComps = new ArrayList(_selSvc.GetSelectedComponents()); + object primarySelection = _selSvc.PrimarySelection; + + //add all control glyphs to all controls on rootComp + _curCompIndex = 0; + _curSelectionBounds = new Rectangle[selComps.Count]; + AddAllControlGlyphs(_rootComponent, selComps, primarySelection); + if (_prevSelectionBounds != null) + { + Region toUpdate = DetermineRegionToRefresh(primarySelection); + using (Graphics g = _behaviorService.AdornerWindowGraphics) + { + if (!toUpdate.IsEmpty(g)) + { + _selectionAdorner.Invalidate(toUpdate); + } + } + } + else + { + // There was no previous selection, so just invalidate the current selection + if (_curSelectionBounds.Length > 0) + { + Rectangle toUpdate = _curSelectionBounds[0]; + for (int i = 1; i < _curSelectionBounds.Length; i++) + { + toUpdate = Rectangle.Union(toUpdate, _curSelectionBounds[i]); + } + if (toUpdate != Rectangle.Empty) + { + _selectionAdorner.Invalidate(toUpdate); + } + } + else + { + _selectionAdorner.Invalidate(); + } + } + + _prevPrimarySelection = primarySelection; + if (_curSelectionBounds.Length > 0) + { + _prevSelectionBounds = new Rectangle[_curSelectionBounds.Length]; + Array.Copy(_curSelectionBounds, _prevSelectionBounds, _curSelectionBounds.Length); + } + else + { + _prevSelectionBounds = null; + } + _selectionChanging = false; + } + } + + /// + /// When a transaction that involves one of our components closes, refresh to reflect any changes. + /// + private void OnTransactionClosed(object sender, DesignerTransactionCloseEventArgs e) + { + if (e.LastTransaction && NeedRefresh) + { + Refresh(); + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionStyles.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionStyles.cs new file mode 100644 index 00000000000..03d66484741 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionStyles.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Design +{ + /// + /// Specifies identifiers to use to indicate the style of the selection frame of a component. + /// + [Flags] + internal enum SelectionStyles + { + /// + /// The component is not currently selected. + /// + None = 0, + /// + /// A component is selected and may be dragged around + /// + Selected = 0x01, + /// + /// An alternative selection border, indicating that a component is in active editing mode and that clicking and dragging on the component affects the component itself, not its position in the designer. + /// + Active = 0x02, + + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionUIService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionUIService.cs new file mode 100644 index 00000000000..c67f85273f8 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionUIService.cs @@ -0,0 +1,1960 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using Microsoft.Win32; + +namespace System.Windows.Forms.Design +{ + /// + /// The selection manager handles selection within a form. There is one selection manager for each form or top level designer. A selection consists of an array of components. One component is designated the "primary" selection and is displayed with different grab handles. An individual selection may or may not have UI associated with it. If the selection manager can find a suitable designer that is representing the selection, it will highlight the designer's border. If the merged property set has a location property, the selection's rules will allow movement. Also, if the property set has a size property, the selection's rules will allow for sizing. Grab handles may be drawn around the designer and user interactions involving the selection frame and grab handles are initiated here, but the actual movement of the objects is done in a designer object that implements the ISelectionHandler interface. + /// + internal sealed class SelectionUIService : Control, ISelectionUIService + { + private static readonly Point s_invalidPoint = new Point(int.MinValue, int.MinValue); + private const int HITTEST_CONTAINER_SELECTOR = 0x0001; + private const int HITTEST_NORMAL_SELECTION = 0x0002; + private const int HITTEST_DEFAULT = HITTEST_CONTAINER_SELECTOR | HITTEST_NORMAL_SELECTION; + + // These are used during a drag operation, either through our own handle drag or through ISelectionUIService + private ISelectionUIHandler _dragHandler; // the current drag handler + private object[] _dragComponents; // the controls being dragged + private SelectionRules _dragRules; // movement constraints for the drag + private bool _dragMoved = false; + private object _containerDrag; // object being dragged during a container drag + // These are used during a drag of a selection grab handle + private bool _ignoreCaptureChanged = false; + private int _mouseDragHitTest; // where the hit occurred that caused the drag + private Point _mouseDragAnchor = s_invalidPoint; // anchor point of the drag + private Rectangle _mouseDragOffset = Rectangle.Empty; // current drag offset + private Point _lastMoveScreenCoord = Point.Empty; + private bool _ctrlSelect = false; // was the CTRL key down when the drag began + private bool _mouseDragging = false; // Are we actually doing a drag? + private ContainerSelectorActiveEventHandler _containerSelectorActive; // the event we fire when user interacts with container selector + private Hashtable _selectionItems; + private readonly Hashtable _selectionHandlers; // Component UI handlers + + private bool _savedVisible; // we stash this when we mess with visibility ourselves. + private bool _batchMode; + private bool _batchChanged; + private bool _batchSync; + private readonly ISelectionService _selSvc; + private readonly IDesignerHost _host; + private DesignerTransaction _dragTransaction; + + /// + /// Creates a new selection manager object. The selection manager manages all selection of all designers under the current form file. + /// + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + public SelectionUIService(IDesignerHost host) : base() + { + SetStyle(ControlStyles.StandardClick | ControlStyles.Opaque | ControlStyles.OptimizedDoubleBuffer, true); + _host = host; + _dragHandler = null; + _dragComponents = null; + _selectionItems = new Hashtable(); + _selectionHandlers = new Hashtable(); + AllowDrop = true; + // Not really any reason for this, except that it can be handy when using Spy++ + Text = "SelectionUIOverlay"; + + _selSvc = (ISelectionService)host.GetService(typeof(ISelectionService)); + if (_selSvc != null) + { + _selSvc.SelectionChanged += new EventHandler(OnSelectionChanged); + } + + // And configure the events we want to listen to. + host.TransactionOpened += new EventHandler(OnTransactionOpened); + host.TransactionClosed += new DesignerTransactionCloseEventHandler(OnTransactionClosed); + if (host.InTransaction) + { + OnTransactionOpened(host, EventArgs.Empty); + } + + IComponentChangeService cs = (IComponentChangeService)host.GetService(typeof(IComponentChangeService)); + if (cs != null) + { + cs.ComponentRemoved += new ComponentEventHandler(OnComponentRemove); + cs.ComponentChanged += new ComponentChangedEventHandler(OnComponentChanged); + } + + // Listen to the SystemEvents so that we can resync selection based on display settings etc. + SystemEvents.DisplaySettingsChanged += new EventHandler(OnSystemSettingChanged); + SystemEvents.InstalledFontsChanged += new EventHandler(OnSystemSettingChanged); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + } + + /// + /// override of control. + /// + protected override CreateParams CreateParams + { + get + { + CreateParams cp = base.CreateParams; + cp.Style &= ~(NativeMethods.WS_CLIPSIBLINGS | NativeMethods.WS_CLIPCHILDREN); + return cp; + } + } + + /// + /// Called to initiate a mouse drag on the selection overlay. We cache some state here. + /// + private void BeginMouseDrag(Point anchor, int hitTest) + { + Capture = true; + _ignoreCaptureChanged = false; + _mouseDragAnchor = anchor; + _mouseDragging = true; + _mouseDragHitTest = hitTest; + _mouseDragOffset = new Rectangle(); + _savedVisible = Visible; + } + + /// + /// Displays the given exception to the user. + /// + private void DisplayError(Exception e) + { + IUIService uis = (IUIService)_host.GetService(typeof(IUIService)); + if (uis != null) + { + uis.ShowError(e); + } + else + { + string message = e.Message; + if (message == null || message.Length == 0) + { + message = e.ToString(); + } + RTLAwareMessageBox.Show(null, message, null, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1, 0); + } + } + + /// + /// Disposes the entire selection UI manager. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_selSvc != null) + { + _selSvc.SelectionChanged -= new EventHandler(OnSelectionChanged); + } + + if (_host != null) + { + _host.TransactionOpened -= new EventHandler(OnTransactionOpened); + _host.TransactionClosed -= new DesignerTransactionCloseEventHandler(OnTransactionClosed); + if (_host.InTransaction) + { + OnTransactionClosed(_host, new DesignerTransactionCloseEventArgs(true, true)); + } + + IComponentChangeService cs = (IComponentChangeService)_host.GetService(typeof(IComponentChangeService)); + if (cs != null) + { + cs.ComponentRemoved -= new ComponentEventHandler(OnComponentRemove); + cs.ComponentChanged -= new ComponentChangedEventHandler(OnComponentChanged); + } + } + + foreach (SelectionUIItem s in _selectionItems.Values) + { + s.Dispose(); + } + _selectionHandlers.Clear(); + _selectionItems.Clear(); + // Listen to the SystemEvents so that we can resync selection based on display settings etc. + SystemEvents.DisplaySettingsChanged -= new EventHandler(OnSystemSettingChanged); + SystemEvents.InstalledFontsChanged -= new EventHandler(OnSystemSettingChanged); + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + } + base.Dispose(disposing); + } + + /// + /// Called when we want to finish a mouse drag and clean up our variables. We call this from multiple places, depending on the state of the finish. This does NOT end the drag -- for that must call EndDrag. This just cleans up the state of the mouse. + /// + private void EndMouseDrag(Point position) + { + // it's possible for us to be destroyed in a drag -- e.g. if this is the tray's selectionuiservice and the last item is dragged out, so check diposed first + if (IsDisposed) + { + return; + } + _ignoreCaptureChanged = true; + Capture = false; + _mouseDragAnchor = s_invalidPoint; + _mouseDragOffset = Rectangle.Empty; + _mouseDragHitTest = 0; + _dragMoved = false; + SetSelectionCursor(position); + _mouseDragging = _ctrlSelect = false; + } + + /// + /// Determines the selection hit test at the given point. The point should be in screen coordinates. + /// + private HitTestInfo GetHitTest(Point value, int flags) + { + Point pt = PointToClient(value); + foreach (SelectionUIItem item in _selectionItems.Values) + { + if ((flags & HITTEST_CONTAINER_SELECTOR) != 0) + { + if (item is ContainerSelectionUIItem && (item.GetRules() & SelectionRules.Visible) != SelectionRules.None) + { + int hitTest = item.GetHitTest(pt); + if ((hitTest & SelectionUIItem.CONTAINER_SELECTOR) != 0) + { + return new HitTestInfo(hitTest, item, true); + } + } + } + + if ((flags & HITTEST_NORMAL_SELECTION) != 0) + { + if (!(item is ContainerSelectionUIItem) && (item.GetRules() & SelectionRules.Visible) != SelectionRules.None) + { + int hitTest = item.GetHitTest(pt); + if (hitTest != SelectionUIItem.NOHIT) + { + if (hitTest != 0) + { + return new HitTestInfo(hitTest, item); + } + else + { + return new HitTestInfo(SelectionUIItem.NOHIT, item); + } + } + } + } + } + return new HitTestInfo(SelectionUIItem.NOHIT, null); + } + + private ISelectionUIHandler GetHandler(object component) + { + return (ISelectionUIHandler)_selectionHandlers[component]; + } + + /// + /// This method returns a well-formed name for a drag transaction based on the rules it is given. + /// + public static string GetTransactionName(SelectionRules rules, object[] objects) + { + // Determine a nice name for the drag operation + string transactionName; + if ((int)(rules & SelectionRules.Moveable) != 0) + { + if (objects.Length > 1) + { + transactionName = string.Format(SR.DragDropMoveComponents, objects.Length); + } + else + { + string name = string.Empty; + if (objects.Length > 0) + { + if (objects[0] is IComponent comp && comp.Site != null) + { + name = comp.Site.Name; + } + else + { + name = objects[0].GetType().Name; + } + } + transactionName = string.Format(SR.DragDropMoveComponent, name); + } + } + else if ((int)(rules & SelectionRules.AllSizeable) != 0) + { + if (objects.Length > 1) + { + transactionName = string.Format(SR.DragDropSizeComponents, objects.Length); + } + else + { + string name = string.Empty; + if (objects.Length > 0) + { + if (objects[0] is IComponent comp && comp.Site != null) + { + name = comp.Site.Name; + } + else + { + name = objects[0].GetType().Name; + } + } + transactionName = string.Format(SR.DragDropSizeComponent, name); + } + } + else + { + transactionName = string.Format(SR.DragDropDragComponents, objects.Length); + } + return transactionName; + } + + /// + /// Called by the designer host when it is entering or leaving a batch operation. Here we queue up selection notification and we turn off our UI. + /// + private void OnTransactionClosed(object sender, DesignerTransactionCloseEventArgs e) + { + if (e.LastTransaction) + { + _batchMode = false; + if (_batchChanged) + { + _batchChanged = false; + ((ISelectionUIService)this).SyncSelection(); + } + if (_batchSync) + { + _batchSync = false; + ((ISelectionUIService)this).SyncComponent(null); + } + } + } + + /// + /// Called by the designer host when it is entering or leaving a batch operation. Here we queue up selection notification and we turn off our UI. + /// + private void OnTransactionOpened(object sender, EventArgs e) + { + _batchMode = true; + } + + /// + /// update our window region on first create. We shouldn't do this before the handle is created or else we will force creation. + /// + protected override void OnHandleCreated(EventArgs e) + { + Debug.Assert(!RecreatingHandle, "Perf hit: we are recreating the docwin handle"); + base.OnHandleCreated(e); + // Default the shape of the control to be empty, so that if nothing is initially selected that our window surface doesn't interfere. + UpdateWindowRegion(); + } + + /// + /// Called whenever a component changes. Here we update our selection information so that the selection rectangles are all up to date. + /// + private void OnComponentChanged(object sender, ComponentChangedEventArgs ccevent) + { + if (!_batchMode) + { + ((ISelectionUIService)this).SyncSelection(); + } + else + { + _batchChanged = true; + } + } + + /// + /// called by the formcore when someone has removed a component. This will remove any selection on the component without disturbing the rest of the selection + /// + private void OnComponentRemove(object sender, ComponentEventArgs ce) + { + _selectionHandlers.Remove(ce.Component); + _selectionItems.Remove(ce.Component); + ((ISelectionUIService)this).SyncComponent(ce.Component); + } + + /// + /// Called to invoke the container active event, if a designer has bound to it. + /// + private void OnContainerSelectorActive(ContainerSelectorActiveEventArgs e) + { + _containerSelectorActive?.Invoke(this, e); + } + + /// + /// Called when the selection changes. We sync up the UI with the selection at this point. + /// + private void OnSelectionChanged(object sender, EventArgs e) + { + ICollection selection = _selSvc.GetSelectedComponents(); + Hashtable newSelection = new Hashtable(selection.Count); + bool shapeChanged = false; + foreach (object comp in selection) + { + object existingItem = _selectionItems[comp]; + bool create = true; + if (existingItem != null) + { + if (existingItem is ContainerSelectionUIItem item) + { + item.Dispose(); + shapeChanged = true; + } + else + { + newSelection[comp] = existingItem; + create = false; + } + } + + if (create) + { + shapeChanged = true; + newSelection[comp] = new SelectionUIItem(this, comp); + } + } + + if (!shapeChanged) + { + shapeChanged = _selectionItems.Keys.Count != newSelection.Keys.Count; + } + + _selectionItems = newSelection; + + if (shapeChanged) + { + UpdateWindowRegion(); + } + Invalidate(); + Update(); + } + + /// + /// User setting requires that we repaint. + /// + private void OnSystemSettingChanged(object sender, EventArgs e) + { + Invalidate(); + } + + /// + /// User setting requires that we repaint. + /// + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + Invalidate(); + } + + /// + /// Inheriting classes should override this method to handle this event. Call super.onDragEnter to send this event to any registered event listeners. + /// + protected override void OnDragEnter(DragEventArgs devent) + { + base.OnDragEnter(devent); + if (_dragHandler != null) + { + _dragHandler.OleDragEnter(devent); + } + } + + /// + /// Inheriting classes should override this method to handle this event. Call super.onDragOver to send this event to any registered event listeners. + /// + protected override void OnDragOver(DragEventArgs devent) + { + base.OnDragOver(devent); + if (_dragHandler != null) + { + _dragHandler.OleDragOver(devent); + } + } + /// + /// Inheriting classes should override this method to handle this event. Call super.onDragLeave to send this event to any registered event listeners. + /// + protected override void OnDragLeave(EventArgs e) + { + base.OnDragLeave(e); + if (_dragHandler != null) + { + _dragHandler.OleDragLeave(); + } + } + + /// + /// Inheriting classes should override this method to handle this event. Call super.onDragDrop to send this event to any registered event listeners. + /// + protected override void OnDragDrop(DragEventArgs devent) + { + base.OnDragDrop(devent); + if (_dragHandler != null) + { + _dragHandler.OleDragDrop(devent); + } + } + + /// + /// Inheriting classes should override this method to handle this event. Call base.OnDoiubleClick to send this event to any registered event listeners. + /// + protected override void OnDoubleClick(EventArgs devent) + { + base.OnDoubleClick(devent); + if (_selSvc != null) + { + object selComp = _selSvc.PrimarySelection; + Debug.Assert(selComp != null, "Illegal selection on double-click"); + if (selComp != null) + { + ISelectionUIHandler handler = GetHandler(selComp); + if (handler != null) + { + handler.OnSelectionDoubleClick((IComponent)selComp); + } + } + } + } + + /// + /// Overrides Control to handle our selection grab handles. + /// + // Standard 'catch all - rethrow critical' exception pattern + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + protected override void OnMouseDown(MouseEventArgs me) + { + if (_dragHandler == null && _selSvc != null) + { + try + { + // First, did the user step on anything? + Point anchor = PointToScreen(new Point(me.X, me.Y)); + HitTestInfo hti = GetHitTest(anchor, HITTEST_DEFAULT); + int hitTest = hti.hitTest; + if ((hitTest & SelectionUIItem.CONTAINER_SELECTOR) != 0) + { + _selSvc.SetSelectedComponents(new object[] { hti.selectionUIHit.component }, SelectionTypes.Auto); + // Then do a drag... + SelectionRules rules = SelectionRules.Moveable; + if (((ISelectionUIService)this).BeginDrag(rules, anchor.X, anchor.Y)) + { + Visible = false; + _containerDrag = hti.selectionUIHit.component; + BeginMouseDrag(anchor, hitTest); + } + } + else if (hitTest != SelectionUIItem.NOHIT && me.Button == MouseButtons.Left) + { + SelectionRules rules = SelectionRules.None; + // If the CTRL key isn't down, select this component, otherwise, we wait until the mouse up. Make sure the component is selected + _ctrlSelect = (Control.ModifierKeys & Keys.Control) != Keys.None; + if (!_ctrlSelect) + { + _selSvc.SetSelectedComponents(new object[] { hti.selectionUIHit.component }, SelectionTypes.Primary); + } + + if ((hitTest & SelectionUIItem.MOVE_MASK) != 0) + { + rules |= SelectionRules.Moveable; + } + if ((hitTest & SelectionUIItem.SIZE_MASK) != 0) + { + if ((hitTest & (SelectionUIItem.SIZE_X | SelectionUIItem.POS_RIGHT)) == (SelectionUIItem.SIZE_X | SelectionUIItem.POS_RIGHT)) + { + rules |= SelectionRules.RightSizeable; + } + if ((hitTest & (SelectionUIItem.SIZE_X | SelectionUIItem.POS_LEFT)) == (SelectionUIItem.SIZE_X | SelectionUIItem.POS_LEFT)) + { + rules |= SelectionRules.LeftSizeable; + } + if ((hitTest & (SelectionUIItem.SIZE_Y | SelectionUIItem.POS_TOP)) == (SelectionUIItem.SIZE_Y | SelectionUIItem.POS_TOP)) + { + rules |= SelectionRules.TopSizeable; + } + if ((hitTest & (SelectionUIItem.SIZE_Y | SelectionUIItem.POS_BOTTOM)) == (SelectionUIItem.SIZE_Y | SelectionUIItem.POS_BOTTOM)) + { + rules |= SelectionRules.BottomSizeable; + } + + if (((ISelectionUIService)this).BeginDrag(rules, anchor.X, anchor.Y)) + { + BeginMouseDrag(anchor, hitTest); + } + } + else + { + // Our mouse is in drag mode. We defer the actual move until the user moves the mouse. + _dragRules = rules; + BeginMouseDrag(anchor, hitTest); + } + } + else if (hitTest == SelectionUIItem.NOHIT) + { + _dragRules = SelectionRules.None; + _mouseDragAnchor = s_invalidPoint; + return; + } + } + catch (Exception e) + { + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + else if (e != CheckoutException.Canceled) + { + DisplayError(e); + } + } + } + } + + /// + /// Overrides Control to handle our selection grab handles. + /// + protected override void OnMouseMove(MouseEventArgs me) + { + base.OnMouseMove(me); + Point screenCoord = PointToScreen(new Point(me.X, me.Y)); + HitTestInfo hti = GetHitTest(screenCoord, HITTEST_CONTAINER_SELECTOR); + int hitTest = hti.hitTest; + if (hitTest != SelectionUIItem.CONTAINER_SELECTOR && hti.selectionUIHit != null) + { + OnContainerSelectorActive(new ContainerSelectorActiveEventArgs(hti.selectionUIHit.component)); + } + + if (_lastMoveScreenCoord == screenCoord) + { + return; + } + // If we're not dragging then set the cursor correctly. + if (!_mouseDragging) + { + SetSelectionCursor(screenCoord); + } + else + { + // we have to make sure the mouse moved farther than the minimum drag distance before we actually start the drag + if (!((ISelectionUIService)this).Dragging && (_mouseDragHitTest & SelectionUIItem.MOVE_MASK) != 0) + { + Size minDragSize = SystemInformation.DragSize; + if ( + Math.Abs(screenCoord.X - _mouseDragAnchor.X) < minDragSize.Width && + Math.Abs(screenCoord.Y - _mouseDragAnchor.Y) < minDragSize.Height) + { + return; + } + else + { + _ignoreCaptureChanged = true; + if (((ISelectionUIService)this).BeginDrag(_dragRules, _mouseDragAnchor.X, _mouseDragAnchor.Y)) + { + // we're moving, so we don't care about the ctrl key any more + _ctrlSelect = false; + } + else + { + EndMouseDrag(MousePosition); + return; + } + } + } + + Rectangle old = _mouseDragOffset; + if ((_mouseDragHitTest & SelectionUIItem.MOVE_X) != 0) + { + _mouseDragOffset.X = screenCoord.X - _mouseDragAnchor.X; + } + if ((_mouseDragHitTest & SelectionUIItem.MOVE_Y) != 0) + { + _mouseDragOffset.Y = screenCoord.Y - _mouseDragAnchor.Y; + } + if ((_mouseDragHitTest & SelectionUIItem.SIZE_X) != 0) + { + if ((_mouseDragHitTest & SelectionUIItem.POS_LEFT) != 0) + { + _mouseDragOffset.X = screenCoord.X - _mouseDragAnchor.X; + _mouseDragOffset.Width = _mouseDragAnchor.X - screenCoord.X; + } + else + { + _mouseDragOffset.Width = screenCoord.X - _mouseDragAnchor.X; + } + } + if ((_mouseDragHitTest & SelectionUIItem.SIZE_Y) != 0) + { + if ((_mouseDragHitTest & SelectionUIItem.POS_TOP) != 0) + { + _mouseDragOffset.Y = screenCoord.Y - _mouseDragAnchor.Y; + _mouseDragOffset.Height = _mouseDragAnchor.Y - screenCoord.Y; + } + else + { + _mouseDragOffset.Height = screenCoord.Y - _mouseDragAnchor.Y; + } + } + + if (!old.Equals(_mouseDragOffset)) + { + Rectangle delta = _mouseDragOffset; + delta.X -= old.X; + delta.Y -= old.Y; + delta.Width -= old.Width; + delta.Height -= old.Height; + if (delta.X != 0 || delta.Y != 0 || delta.Width != 0 || delta.Height != 0) + { + // Go to default cursor for moves... + if ((_mouseDragHitTest & SelectionUIItem.MOVE_X) != 0 || (_mouseDragHitTest & SelectionUIItem.MOVE_Y) != 0) + { + Cursor = Cursors.Default; + } + ((ISelectionUIService)this).DragMoved(delta); + } + } + } + } + + /// + /// Overrides Control to handle our selection grab handles. + /// + // Standard 'catch all - rethrow critical' exception pattern + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + protected override void OnMouseUp(MouseEventArgs me) + { + try + { + Point screenCoord = PointToScreen(new Point(me.X, me.Y)); + if (_ctrlSelect && !_mouseDragging && _selSvc != null) + { + HitTestInfo hti = GetHitTest(screenCoord, HITTEST_DEFAULT); + _selSvc.SetSelectedComponents(new object[] { hti.selectionUIHit.component }, SelectionTypes.Primary); + } + + if (_mouseDragging) + { + object oldContainerDrag = _containerDrag; + bool oldDragMoved = _dragMoved; + EndMouseDrag(screenCoord); + if (((ISelectionUIService)this).Dragging) + { + ((ISelectionUIService)this).EndDrag(false); + } + + if (me.Button == MouseButtons.Right && oldContainerDrag != null && !oldDragMoved) + { + OnContainerSelectorActive(new ContainerSelectorActiveEventArgs(oldContainerDrag, ContainerSelectorActiveEventArgsType.Contextmenu)); + } + } + } + catch (Exception e) + { + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + else if (e != CheckoutException.Canceled) + { + DisplayError(e); + } + } + } + + /// + /// If the selection manager move, this indicates that the form has autoscolling enabled and has been scrolled. We have to invalidate here because we may get moved before the rest of the components so we may draw the selection in the wrong spot. + /// + protected override void OnMove(EventArgs e) + { + base.OnMove(e); + Invalidate(); + } + + /// + /// overrides control.onPaint. here we paint the selection handles. The window's region was setup earlier. + /// + protected override void OnPaint(PaintEventArgs e) + { + // Paint the regular selection items first, and then the container selectors last so they draw over the top. + foreach (SelectionUIItem item in _selectionItems.Values) + { + if (item is ContainerSelectionUIItem) + { + continue; + } + item.DoPaint(e.Graphics); + } + + foreach (SelectionUIItem item in _selectionItems.Values) + { + if (item is ContainerSelectionUIItem) + { + item.DoPaint(e.Graphics); + } + } + } + + /// + /// Sets the appropriate selection cursor at the given point. + /// + private void SetSelectionCursor(Point pt) + { + Point clientCoords = PointToClient(pt); + // We render the cursor in the same order we paint. + foreach (SelectionUIItem item in _selectionItems.Values) + { + if (item is ContainerSelectionUIItem) + { + continue; + } + Cursor cursor = item.GetCursorAtPoint(clientCoords); + if (cursor != null) + { + if (cursor == Cursors.Default) + { + Cursor = null; + } + else + { + Cursor = cursor; + } + return; + } + } + + foreach (SelectionUIItem item in _selectionItems.Values) + { + if (item is ContainerSelectionUIItem) + { + Cursor cursor = item.GetCursorAtPoint(clientCoords); + if (cursor != null) + { + if (cursor == Cursors.Default) + { + Cursor = null; + } + else + { + Cursor = cursor; + } + return; + } + } + } + // Don't know what to set; just use the default. + Cursor = null; + } + + /// + /// called when the overlay region is invalid and should be updated + /// + private void UpdateWindowRegion() + { + Region region = new Region(new Rectangle(0, 0, 0, 0)); + foreach (SelectionUIItem item in _selectionItems.Values) + { + region.Union(item.GetRegion()); + } + + Region = region; + } + + /// + /// Override of our control's WNDPROC. We diddle with capture a bit, and it's important to turn this off if the capture changes. + /// + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_LBUTTONUP: + case NativeMethods.WM_RBUTTONUP: + if (_mouseDragAnchor != s_invalidPoint) + { + _ignoreCaptureChanged = true; + } + break; + case NativeMethods.WM_CAPTURECHANGED: + if (!_ignoreCaptureChanged && _mouseDragAnchor != s_invalidPoint) + { + EndMouseDrag(MousePosition); + if (((ISelectionUIService)this).Dragging) + { + ((ISelectionUIService)this).EndDrag(true); + } + } + _ignoreCaptureChanged = false; + break; + } + base.WndProc(ref m); + } + + /// + /// This can be used to determine if the user is in the middle of a drag operation. + /// + bool ISelectionUIService.Dragging + { + get => _dragHandler != null; + } + + /// + /// Determines if the selection UI is shown or not. + /// + bool ISelectionUIService.Visible + { + get => Visible; + set => Visible = value; + } + + /// + /// Adds an event handler to the ContainerSelectorActive event. This event is fired whenever the user interacts with the container selector in a manor that would indicate that the selector should continued to be displayed. Since the container selector normally will vanish after a timeout, designers should listen to this event and reset the timeout when this event occurs. + /// + event ContainerSelectorActiveEventHandler ISelectionUIService.ContainerSelectorActive + { + add => _containerSelectorActive += value; + remove => _containerSelectorActive -= value; + } + + /// + /// Assigns a selection UI handler to a given component. The handler will be called when the UI service needs information about the component. A single selection UI handler can be assigned to multiple components. When multiple components are dragged, only a single handler may control the drag. Because of this, only components that are assigned the same handler as the primary selection are included in drag operations. A selection UI handler is automatically unassigned when the component is removed from the container or disposed. + /// + void ISelectionUIService.AssignSelectionUIHandler(object component, ISelectionUIHandler handler) + { + ISelectionUIHandler oldHandler = (ISelectionUIHandler)_selectionHandlers[component]; + if (oldHandler != null) + { + // The collection editors do not dispose objects from the collection before setting a new collection. This causes items that are common to the old and new collections to come through this code path again, causing the exception to fire. So, we check to see if the SelectionUIHandler is same, and bail out in that case. + if (handler == oldHandler) + { + return; + } + Debug.Fail("A component may have only one selection UI handler."); + throw new InvalidOperationException(); + } + _selectionHandlers[component] = handler; + // If this component is selected, create a new UI handler for it. + if (_selSvc != null && _selSvc.GetComponentSelected(component)) + { + SelectionUIItem item = new SelectionUIItem(this, component); + _selectionItems[component] = item; + UpdateWindowRegion(); + item.Invalidate(); + } + } + + void ISelectionUIService.ClearSelectionUIHandler(object component, ISelectionUIHandler handler) + { + ISelectionUIHandler oldHandler = (ISelectionUIHandler)_selectionHandlers[component]; + if (oldHandler == handler) + { + _selectionHandlers[component] = null; + } + } + + /// + /// This can be called by an outside party to begin a drag of the currently selected set of components. + /// + // Standard 'catch all - rethrow critical' exception pattern + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + bool ISelectionUIService.BeginDrag(SelectionRules rules, int initialX, int initialY) + { + if (_dragHandler != null) + { + Debug.Fail("Caller is starting a drag, but there is already one in progress -- we cannot nest these!"); + return false; + } + + if (rules == SelectionRules.None) + { + Debug.Fail("Caller is starting requesting a drag with no drag rules."); + return false; + } + + if (_selSvc == null) + { + return false; + } + + _savedVisible = Visible; + // First, get the list of controls + ICollection col = _selSvc.GetSelectedComponents(); + object[] objects = new object[col.Count]; + col.CopyTo(objects, 0); + objects = ((ISelectionUIService)this).FilterSelection(objects, rules); + if (objects.Length == 0) + { + return false; // nothing selected + } + + // We allow all components with the same UI handler as the primary selection to participate in the drag. + ISelectionUIHandler primaryHandler = null; + object primary = _selSvc.PrimarySelection; + if (primary != null) + { + primaryHandler = GetHandler(primary); + } + if (primaryHandler == null) + { + return false; // no UI handler for selection + } + + // Now within the given selection, add those items that have the same UI handler and that have the proper rule constraints. + ArrayList list = new ArrayList(); + for (int i = 0; i < objects.Length; i++) + { + if (GetHandler(objects[i]) == primaryHandler) + { + SelectionRules compRules = primaryHandler.GetComponentRules(objects[i]); + if ((compRules & rules) == rules) + { + list.Add(objects[i]); + } + } + } + if (list.Count == 0) + { + return false; // nothing matching the given constraints + } + objects = list.ToArray(); + bool dragging = false; + // We must setup state before calling QueryBeginDrag. It is possible that QueryBeginDrag will cancel a drag (if it places a modal dialog, for example), so we must have the drag data all setup before it cancels. Then, we will check again after QueryBeginDrag to see if a cancel happened. + _dragComponents = objects; + _dragRules = rules; + _dragHandler = primaryHandler; + string transactionName = GetTransactionName(rules, objects); + _dragTransaction = _host.CreateTransaction(transactionName); + try + { + if (primaryHandler.QueryBeginDrag(objects, rules, initialX, initialY)) + { + if (_dragHandler != null) + { + try + { + dragging = primaryHandler.BeginDrag(objects, rules, initialX, initialY); + } + catch (Exception e) + { + Debug.Fail("Drag handler threw during BeginDrag -- bad handler!", e.ToString()); + dragging = false; + } + } + } + } + finally + { + + if (!dragging) + { + _dragComponents = null; + _dragRules = 0; + _dragHandler = null; + + // Always commit this -- BeginDrag returns false for our drags because it is a complete operation. + if (_dragTransaction != null) + { + _dragTransaction.Commit(); + _dragTransaction = null; + } + } + } + return dragging; + } + + /// + /// Called by an outside party to update drag information. This can only be called after a successful call to beginDrag. + /// + void ISelectionUIService.DragMoved(Rectangle offset) + { + Rectangle newOffset = Rectangle.Empty; + if (_dragHandler == null) + { + throw new Exception(SR.DesignerBeginDragNotCalled); + } + + Debug.Assert(_dragComponents != null, "We should have a set of drag controls here"); + if ((_dragRules & SelectionRules.Moveable) == SelectionRules.None + && (_dragRules & (SelectionRules.TopSizeable | SelectionRules.LeftSizeable)) == SelectionRules.None) + { + newOffset = new Rectangle(0, 0, offset.Width, offset.Height); + } + if ((_dragRules & SelectionRules.AllSizeable) == SelectionRules.None) + { + if (newOffset.IsEmpty) + { + newOffset = new Rectangle(offset.X, offset.Y, 0, 0); + } + else + { + newOffset.Width = newOffset.Height = 0; + } + } + + if (!newOffset.IsEmpty) + { + offset = newOffset; + } + + Visible = false; + _dragMoved = true; + _dragHandler.DragMoved(_dragComponents, offset); + } + + /// + /// Called by an outside party to finish a drag operation. This can only be called after a successful call to beginDrag. + /// + // Standard 'catch all - rethrow critical' exception pattern + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + void ISelectionUIService.EndDrag(bool cancel) + { + _containerDrag = null; + ISelectionUIHandler handler = _dragHandler; + object[] components = _dragComponents; + // Clean these out so that even if we throw an exception we don't die. + _dragHandler = null; + _dragComponents = null; + _dragRules = SelectionRules.None; + if (handler == null) + { + throw new InvalidOperationException(); + } + + // Typically, the handler will be changing a bunch of component properties here. Optimize this by enclosing it within a batch call. + DesignerTransaction trans = null; + try + { + IComponent comp = components[0] as IComponent; + if (components.Length > 1 || (components.Length == 1 && comp != null && comp.Site == null)) + { + trans = _host.CreateTransaction(string.Format(SR.DragDropMoveComponents, components.Length)); + } + else if (components.Length == 1) + { + if (comp != null) + { + trans = _host.CreateTransaction(string.Format(SR.DragDropMoveComponent, comp.Site.Name)); + } + } + try + { + handler.EndDrag(components, cancel); + } + catch (Exception e) + { + Debug.Fail(e.ToString()); + } + } + finally + { + if (trans != null) + trans.Commit(); + // Reset the selection. This will re-display our selection. + Visible = _savedVisible; + ((ISelectionUIService)this).SyncSelection(); + if (_dragTransaction != null) + { + _dragTransaction.Commit(); + _dragTransaction = null; + } + // In case this drag was initiated by us, ensure that our mouse state is correct + EndMouseDrag(MousePosition); + } + } + + /// + /// Filters the set of selected components. The selection service will retrieve all components that are currently selected. This method allows you to filter thisset down to components that match your criteria. The selectionRules parameter must contain one or more flags from the SelectionRules class. These flags allow you to constrain the set of selected objects to visible, movable, sizeable or all objects. + /// + object[] ISelectionUIService.FilterSelection(object[] components, SelectionRules selectionRules) + { + object[] selection = null; + if (components == null) + return new object[0]; + // Mask off any selection object that doesn't adhere to the given ruleset. We can ignore this if the ruleset is zero, as all components would be accepted. + if (selectionRules != SelectionRules.None) + { + ArrayList list = new ArrayList(); + foreach (object comp in components) + { + SelectionUIItem item = (SelectionUIItem)_selectionItems[comp]; + if (item != null && !(item is ContainerSelectionUIItem)) + { + if ((item.GetRules() & selectionRules) == selectionRules) + { + list.Add(comp); + } + } + } + selection = (object[])list.ToArray(); + } + + return selection ?? (new object[0]); + } + + /// + /// Retrieves the width and height of a selection border grab handle. Designers may need this to properly position their user interfaces. + /// + Size ISelectionUIService.GetAdornmentDimensions(AdornmentType adornmentType) + { + switch (adornmentType) + { + case AdornmentType.GrabHandle: + return new Size(SelectionUIItem.GRABHANDLE_WIDTH, SelectionUIItem.GRABHANDLE_HEIGHT); + case AdornmentType.ContainerSelector: + case AdornmentType.Maximum: + return new Size(ContainerSelectionUIItem.CONTAINER_WIDTH, ContainerSelectionUIItem.CONTAINER_HEIGHT); + } + return new Size(0, 0); + } + + /// + /// Tests to determine if the given screen coordinate is over an adornment for the specified component. This will only return true if the adornment, and selection UI, is visible. + /// + bool ISelectionUIService.GetAdornmentHitTest(object component, Point value) => GetHitTest(value, HITTEST_DEFAULT).hitTest != SelectionUIItem.NOHIT; + + /// + /// Determines if the component is currently "container" selected. Container selection is a visual aid for selecting containers. It doesn't affect the normal "component" selection. + /// + bool ISelectionUIService.GetContainerSelected(object component) => (component != null && _selectionItems[component] is ContainerSelectionUIItem); + + /// + /// Retrieves a set of flags that define rules for the selection. Selection rules indicate if the given component can be moved or sized, for example. + /// + SelectionRules ISelectionUIService.GetSelectionRules(object component) + { + SelectionUIItem sel = (SelectionUIItem)_selectionItems[component]; + if (sel == null) + { + Debug.Fail("The component is not currently selected."); + throw new InvalidOperationException(); + } + return sel.GetRules(); + } + + /// + /// Allows you to configure the style of the selection frame that a component uses. This is useful if your component supports different modes of operation (such as an in-place editing mode and a static design mode). Where possible, you should leave the selection style as is and use the design-time hit testing feature of the IDesigner interface to provide features at design time. The value of style must be one of the SelectionStyle enum values. The selection style is only valid for the duration that the component is selected. + /// + SelectionStyles ISelectionUIService.GetSelectionStyle(object component) + { + SelectionUIItem s = (SelectionUIItem)_selectionItems[component]; + if (s == null) + { + return SelectionStyles.None; + } + return s.Style; + } + + /// + /// Changes the container selection status of the given component. Container selection is a visual aid for selecting containers. It doesn't affect the normal "component" selection. + /// + void ISelectionUIService.SetContainerSelected(object component, bool selected) + { + if (selected) + { + SelectionUIItem existingItem = (SelectionUIItem)_selectionItems[component]; + if (!(existingItem is ContainerSelectionUIItem)) + { + if (existingItem != null) + { + existingItem.Dispose(); + } + SelectionUIItem item = new ContainerSelectionUIItem(this, component); + _selectionItems[component] = item; + // Now update our region and invalidate + UpdateWindowRegion(); + if (existingItem != null) + { + existingItem.Invalidate(); + } + item.Invalidate(); + } + } + else + { + SelectionUIItem existingItem = (SelectionUIItem)_selectionItems[component]; + if (existingItem == null || existingItem is ContainerSelectionUIItem) + { + _selectionItems.Remove(component); + if (existingItem != null) + { + existingItem.Dispose(); + } + UpdateWindowRegion(); + existingItem.Invalidate(); + } + } + } + + /// + /// Allows you to configure the style of the selection frame that a component uses. This is useful if your component supports different modes of operation (such as an in-place editing mode and a static design mode). Where possible, you should leave the selection style as is and use the design-time hit testing feature of the IDesigner interface to provide features at design time. The value of style must be one of the SelectionStyle enum values. The selection style is only valid for the duration that the component is selected. + /// + void ISelectionUIService.SetSelectionStyle(object component, SelectionStyles style) + { + SelectionUIItem selUI = (SelectionUIItem)_selectionItems[component]; + if (_selSvc != null && _selSvc.GetComponentSelected(component)) + { + selUI = new SelectionUIItem(this, component); + _selectionItems[component] = selUI; + } + + if (selUI != null) + { + selUI.Style = style; + UpdateWindowRegion(); + selUI.Invalidate(); + } + } + + /// + /// This should be called when a component has been moved, sized or re-parented, but the change was not the result of a property change. All property changes are monitored by the selection UI service, so this is automatic most of the time. There are times, however, when a component may be moved without a property change notification occurring. Scrolling an auto scroll Win32 form is an example of this. This method simply re-queries all currently selected components for their bounds and udpates the selection handles for any that have changed. + /// + void ISelectionUIService.SyncSelection() + { + if (_batchMode) + { + _batchChanged = true; + } + else + { + if (IsHandleCreated) + { + bool updateRegion = false; + foreach (SelectionUIItem item in _selectionItems.Values) + { + updateRegion |= item.UpdateSize(); + item.UpdateRules(); + } + if (updateRegion) + { + UpdateWindowRegion(); + Update(); + } + } + } + } + + /// + /// This should be called when a component's property changed, that the designer thinks should result in a selection UI change. This method simply re-queries all currently selected components for their bounds and udpates the selection handles for any that have changed. + /// + void ISelectionUIService.SyncComponent(object component) + { + if (_batchMode) + { + _batchSync = true; + } + else + { + if (IsHandleCreated) + { + foreach (SelectionUIItem item in _selectionItems.Values) + { + item.UpdateRules(); + item.Dispose(); + } + UpdateWindowRegion(); + Invalidate(); + Update(); + } + } + } + + /// + /// This class represents a single selected object. + /// + private class SelectionUIItem + { + // Flags describing how a given selection point may be sized + public const int SIZE_X = 0x0001; + public const int SIZE_Y = 0x0002; + public const int SIZE_MASK = 0x0003; + // Flags describing how a given selection point may be moved + public const int MOVE_X = 0x0004; + public const int MOVE_Y = 0x0008; + public const int MOVE_MASK = 0x000C; + // Flags describing where a given selection point is located on an object + public const int POS_LEFT = 0x0010; + public const int POS_TOP = 0x0020; + public const int POS_RIGHT = 0x0040; + public const int POS_BOTTOM = 0x0080; + public const int POS_MASK = 0x00F0; + // This is returned if the given selection point is not within the selection + public const int NOHIT = 0x0100; + // This is returned if the given selection point on the "container selector" + public const int CONTAINER_SELECTOR = 0x0200; + public const int GRABHANDLE_WIDTH = 7; + public const int GRABHANDLE_HEIGHT = 7; + // tables we use to determine how things can move and size + internal static readonly int[] activeSizeArray = new int[] { + SIZE_X | SIZE_Y | POS_LEFT | POS_TOP, SIZE_Y | POS_TOP, SIZE_X | SIZE_Y | POS_TOP | POS_RIGHT, + SIZE_X | POS_LEFT, SIZE_X | POS_RIGHT, + SIZE_X | SIZE_Y | POS_LEFT | POS_BOTTOM, SIZE_Y | POS_BOTTOM, SIZE_X | SIZE_Y | POS_RIGHT | POS_BOTTOM + }; + + internal static readonly Cursor[] activeCursorArrays = new Cursor[] { + Cursors.SizeNWSE, Cursors.SizeNS, Cursors.SizeNESW, + Cursors.SizeWE, Cursors.SizeWE, + Cursors.SizeNESW, Cursors.SizeNS, Cursors.SizeNWSE + }; + + internal static readonly int[] inactiveSizeArray = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }; + internal static readonly Cursor[] inactiveCursorArray = new Cursor[] { + Cursors.Arrow, Cursors.Arrow, Cursors.Arrow, + Cursors.Arrow, Cursors.Arrow, + Cursors.Arrow, Cursors.Arrow, Cursors.Arrow + }; + + internal int[] sizes; // array of sizing rules for this selection + internal Cursor[] cursors; // array of cursors for each grab location + internal SelectionUIService selUIsvc; + internal Rectangle innerRect = Rectangle.Empty; // inner part of selection (== control bounds) + internal Rectangle outerRect = Rectangle.Empty; // outer part of selection (inner + border size) + internal Region region; // region object that defines the shape + internal object component; // the component we're rendering + private readonly Control _control; + private SelectionStyles _selectionStyle; // how do we draw this thing? + private SelectionRules _selectionRules; + private readonly ISelectionUIHandler _handler; // the components selection UI handler (can be null) + + /// Its ok to call virtual method as this is a private class. + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public SelectionUIItem(SelectionUIService selUIsvc, object component) + { + this.selUIsvc = selUIsvc; + this.component = component; + _selectionStyle = SelectionStyles.Selected; + // By default, a component isn't visible. We must establish what it can do through it's UI handler. + _handler = selUIsvc.GetHandler(component); + sizes = inactiveSizeArray; + cursors = inactiveCursorArray; + if (component is IComponent comp) + { + if (selUIsvc._host.GetDesigner(comp) is ControlDesigner cd) + { + _control = cd.Control; + } + } + UpdateRules(); + UpdateGrabSettings(); + UpdateSize(); + } + + /// + /// Retrieves the style of the selection frame for this selection. + /// + public virtual SelectionStyles Style + { + get => _selectionStyle; + set + { + if (value != _selectionStyle) + { + _selectionStyle = value; + if (region != null) + { + region.Dispose(); + region = null; + } + } + } + } + + /// + /// paints the selection + /// + public virtual void DoPaint(Graphics gr) + { + // If we're not visible, then there's nothing to do... + // + if ((GetRules() & SelectionRules.Visible) == SelectionRules.None) + return; + bool fActive = false; + if (selUIsvc._selSvc != null) + { + fActive = component == selUIsvc._selSvc.PrimarySelection; + // Office rules: If this is a multi-select, reverse the colors for active / inactive. + fActive = (fActive == (selUIsvc._selSvc.SelectionCount <= 1)); + } + + Rectangle r = new Rectangle(outerRect.X, outerRect.Y, GRABHANDLE_WIDTH, GRABHANDLE_HEIGHT); + Rectangle inner = innerRect; + Rectangle outer = outerRect; + Region oldClip = gr.Clip; + Color borderColor = SystemColors.Control; + if (_control != null && _control.Parent != null) + { + Control parent = _control.Parent; + borderColor = parent.BackColor; + } + Brush brush = new SolidBrush(borderColor); + gr.ExcludeClip(inner); + gr.FillRectangle(brush, outer); + brush.Dispose(); + gr.Clip = oldClip; + ControlPaint.DrawSelectionFrame(gr, false, outer, inner, borderColor); + //if it's not locked & it is sizeable... + if (((GetRules() & SelectionRules.Locked) == SelectionRules.None) && (GetRules() & SelectionRules.AllSizeable) != SelectionRules.None) + { + // upper left + ControlPaint.DrawGrabHandle(gr, r, fActive, (sizes[0] != 0)); + // upper right + r.X = inner.X + inner.Width; + ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[2] != 0); + // lower right + r.Y = inner.Y + inner.Height; + ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[7] != 0); + // lower left + r.X = outer.X; + ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[5] != 0); + // lower middle + r.X += (outer.Width - GRABHANDLE_WIDTH) / 2; + ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[6] != 0); + // upper middle + r.Y = outer.Y; + ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[1] != 0); + // left middle + r.X = outer.X; + r.Y = inner.Y + (inner.Height - GRABHANDLE_HEIGHT) / 2; + ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[3] != 0); + // right middle + r.X = inner.X + inner.Width; + ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[4] != 0); + } + else + { + ControlPaint.DrawLockedFrame(gr, outer, fActive); + } + } + + /// + /// Retrieves an appropriate cursor at the given point. If there is no appropriate cursor here (ie, the point lies outside the selection rectangle), then this will return null. + /// + public virtual Cursor GetCursorAtPoint(Point pt) + { + Cursor cursor = null; + if (PointWithinSelection(pt)) + { + int nOffset = -1; + if ((GetRules() & SelectionRules.AllSizeable) != SelectionRules.None) + { + nOffset = GetHandleIndexOfPoint(pt); + } + + if (-1 == nOffset) + { + if ((GetRules() & SelectionRules.Moveable) == SelectionRules.None) + { + cursor = Cursors.Default; + } + else + { + cursor = Cursors.SizeAll; + } + } + else + { + cursor = cursors[nOffset]; + } + } + return cursor; + } + + /// + /// returns the hit test code of the given point. This may be one of: + /// + public virtual int GetHitTest(Point pt) + { + // Is it within our rects? + if (!PointWithinSelection(pt)) + { + return NOHIT; + } + + // Which index in the array is this? + int nOffset = GetHandleIndexOfPoint(pt); + // If no index, the user has picked on the hatch + if (-1 == nOffset || sizes[nOffset] == 0) + { + return ((GetRules() & SelectionRules.Moveable) == SelectionRules.None ? 0 : MOVE_X | MOVE_Y); + } + return sizes[nOffset]; + } + + /// + /// gets the array offset of the handle at the given point + /// + private int GetHandleIndexOfPoint(Point pt) + { + if (pt.X >= outerRect.X && pt.X <= innerRect.X) + { + // Something on the left side. + if (pt.Y >= outerRect.Y && pt.Y <= innerRect.Y) + return 0; // top left + if (pt.Y >= innerRect.Y + innerRect.Height && pt.Y <= outerRect.Y + outerRect.Height) + return 5; // bottom left + if (pt.Y >= outerRect.Y + (outerRect.Height - GRABHANDLE_HEIGHT) / 2 + && pt.Y <= outerRect.Y + (outerRect.Height + GRABHANDLE_HEIGHT) / 2) + return 3; // middle left + return -1; // unknown hit + } + + if (pt.Y >= outerRect.Y && pt.Y <= innerRect.Y) + { + // something on the top + Debug.Assert(!(pt.X >= outerRect.X && pt.X <= innerRect.X), "Should be handled by left top check"); + if (pt.X >= innerRect.X + innerRect.Width && pt.X <= outerRect.X + outerRect.Width) + return 2; // top right + if (pt.X >= outerRect.X + (outerRect.Width - GRABHANDLE_WIDTH) / 2 + && pt.X <= outerRect.X + (outerRect.Width + GRABHANDLE_WIDTH) / 2) + return 1; // top middle + return -1; // unknown hit + } + + if (pt.X >= innerRect.X + innerRect.Width && pt.X <= outerRect.X + outerRect.Width) + { + // something on the right side + Debug.Assert(!(pt.Y >= outerRect.Y && pt.Y <= innerRect.Y), "Should be handled by top right check"); + if (pt.Y >= innerRect.Y + innerRect.Height && pt.Y <= outerRect.Y + outerRect.Height) + return 7; // bottom right + if (pt.Y >= outerRect.Y + (outerRect.Height - GRABHANDLE_HEIGHT) / 2 + && pt.Y <= outerRect.Y + (outerRect.Height + GRABHANDLE_HEIGHT) / 2) + return 4; // middle right + return -1; // unknown hit + } + + if (pt.Y >= innerRect.Y + innerRect.Height && pt.Y <= outerRect.Y + outerRect.Height) + { + // something on the bottom + Debug.Assert(!(pt.X >= outerRect.X && pt.X <= innerRect.X), "Should be handled by left bottom check"); + + Debug.Assert(!(pt.X >= innerRect.X + innerRect.Width && pt.X <= outerRect.X + outerRect.Width), "Should be handled by right bottom check"); + + if (pt.X >= outerRect.X + (outerRect.Width - GRABHANDLE_WIDTH) / 2 && pt.X <= outerRect.X + (outerRect.Width + GRABHANDLE_WIDTH) / 2) + return 6; // bottom middle + return -1; // unknown hit + } + return -1; // unknown hit + } + + /// + /// returns a region handle that defines this selection. This is used to piece together a paint region for the surface that we draw our selection handles on + /// + public virtual Region GetRegion() + { + if (region == null) + { + if ((GetRules() & SelectionRules.Visible) != SelectionRules.None && !outerRect.IsEmpty) + { + region = new Region(outerRect); + region.Exclude(innerRect); + } + else + { + region = new Region(new Rectangle(0, 0, 0, 0)); + } + + if (_handler != null) + { + Rectangle handlerClip = _handler.GetSelectionClipRect(component); + if (!handlerClip.IsEmpty) + { + region.Intersect(selUIsvc.RectangleToClient(handlerClip)); + } + } + } + return region; + } + + /// + /// Retrieves the rules associated with this selection. + /// + public SelectionRules GetRules() => _selectionRules; + + public void Dispose() + { + if (region != null) + { + region.Dispose(); + region = null; + } + } + + /// + /// Invalidates the region for this selection glyph. + /// + public void Invalidate() + { + if (!outerRect.IsEmpty && !selUIsvc.Disposing) + { + selUIsvc.Invalidate(outerRect); + } + } + + /// + /// Part of our hit testing logic; determines if the point is somewhere within our selection. + /// + protected bool PointWithinSelection(Point pt) + { + // This is only supported for visible selections + if ((GetRules() & SelectionRules.Visible) == SelectionRules.None || outerRect.IsEmpty || innerRect.IsEmpty) + { + return false; + } + if (pt.X < outerRect.X || pt.X > outerRect.X + outerRect.Width) + { + return false; + } + if (pt.Y < outerRect.Y || pt.Y > outerRect.Y + outerRect.Height) + { + return false; + } + if (pt.X > innerRect.X + && pt.X < innerRect.X + innerRect.Width + && pt.Y > innerRect.Y + && pt.Y < innerRect.Y + innerRect.Height) + { + return false; + } + return true; + } + + /// + /// Updates the available grab handle settings based on the current rules. + /// + private void UpdateGrabSettings() + { + SelectionRules rules = GetRules(); + if ((rules & SelectionRules.AllSizeable) == SelectionRules.None) + { + sizes = inactiveSizeArray; + cursors = inactiveCursorArray; + } + else + { + sizes = new int[8]; + cursors = new Cursor[8]; + Array.Copy(activeCursorArrays, cursors, cursors.Length); + Array.Copy(activeSizeArray, sizes, sizes.Length); + if ((rules & SelectionRules.TopSizeable) != SelectionRules.TopSizeable) + { + sizes[0] = 0; + sizes[1] = 0; + sizes[2] = 0; + cursors[0] = Cursors.Arrow; + cursors[1] = Cursors.Arrow; + cursors[2] = Cursors.Arrow; + } + if ((rules & SelectionRules.LeftSizeable) != SelectionRules.LeftSizeable) + { + sizes[0] = 0; + sizes[3] = 0; + sizes[5] = 0; + cursors[0] = Cursors.Arrow; + cursors[3] = Cursors.Arrow; + cursors[5] = Cursors.Arrow; + } + if ((rules & SelectionRules.BottomSizeable) != SelectionRules.BottomSizeable) + { + sizes[5] = 0; + sizes[6] = 0; + sizes[7] = 0; + cursors[5] = Cursors.Arrow; + cursors[6] = Cursors.Arrow; + cursors[7] = Cursors.Arrow; + } + if ((rules & SelectionRules.RightSizeable) != SelectionRules.RightSizeable) + { + sizes[2] = 0; + sizes[4] = 0; + sizes[7] = 0; + cursors[2] = Cursors.Arrow; + cursors[4] = Cursors.Arrow; + cursors[7] = Cursors.Arrow; + } + } + } + + /// + /// Updates our cached selection rules based on current handler values. + /// + public void UpdateRules() + { + if (_handler == null) + { + _selectionRules = SelectionRules.None; + } + else + { + SelectionRules oldRules = _selectionRules; + _selectionRules = _handler.GetComponentRules(component); + if (_selectionRules != oldRules) + { + UpdateGrabSettings(); + Invalidate(); + } + } + } + + /// + /// rebuilds the inner and outer rectangles based on the current selItem.component dimensions. We could calcuate this every time, but that would be expensive for functions like getHitTest that are called a lot (like on every mouse move) + /// + public virtual bool UpdateSize() + { + bool sizeChanged = false; + // Short circuit common cases + if (_handler == null) + return false; + if ((GetRules() & SelectionRules.Visible) == SelectionRules.None) + return false; + innerRect = _handler.GetComponentBounds(component); + if (!innerRect.IsEmpty) + { + innerRect = selUIsvc.RectangleToClient(innerRect); + Rectangle rcOuterNew = new Rectangle( innerRect.X - GRABHANDLE_WIDTH, innerRect.Y - GRABHANDLE_HEIGHT, innerRect.Width + 2 * GRABHANDLE_WIDTH, innerRect.Height + 2 * GRABHANDLE_HEIGHT); + if (outerRect.IsEmpty || !outerRect.Equals(rcOuterNew)) + { + if (!outerRect.IsEmpty) + Invalidate(); + outerRect = rcOuterNew; + Invalidate(); + if (region != null) + { + region.Dispose(); + region = null; + } + sizeChanged = true; + } + } + else + { + Rectangle rcNew = new Rectangle(0, 0, 0, 0); + sizeChanged = outerRect.IsEmpty || !outerRect.Equals(rcNew); + innerRect = outerRect = rcNew; + } + return sizeChanged; + } + } + + private class ContainerSelectionUIItem : SelectionUIItem + { + public const int CONTAINER_WIDTH = 13; + public const int CONTAINER_HEIGHT = 13; + + public ContainerSelectionUIItem(SelectionUIService selUIsvc, object component) : base(selUIsvc, component) + { + } + + public override Cursor GetCursorAtPoint(Point pt) + { + if ((GetHitTest(pt) & CONTAINER_SELECTOR) != 0 && (GetRules() & SelectionRules.Moveable) != SelectionRules.None) + { + return Cursors.SizeAll; + } + else + { + return null; + } + } + + public override int GetHitTest(Point pt) + { + int ht = NOHIT; + if ((GetRules() & SelectionRules.Visible) != SelectionRules.None && !outerRect.IsEmpty) + { + Rectangle r = new Rectangle(outerRect.X, outerRect.Y, CONTAINER_WIDTH, CONTAINER_HEIGHT); + + if (r.Contains(pt)) + { + ht = CONTAINER_SELECTOR; + if ((GetRules() & SelectionRules.Moveable) != SelectionRules.None) + { + ht |= MOVE_X | MOVE_Y; + } + } + } + return ht; + } + + public override void DoPaint(Graphics gr) + { + // If we're not visible, then there's nothing to do... + if ((GetRules() & SelectionRules.Visible) == SelectionRules.None) + return; + Rectangle glyphBounds = new Rectangle(outerRect.X, outerRect.Y, CONTAINER_WIDTH, CONTAINER_HEIGHT); + ControlPaint.DrawContainerGrabHandle(gr, glyphBounds); + } + + public override Region GetRegion() + { + if (region == null) + { + if ((GetRules() & SelectionRules.Visible) != SelectionRules.None && !outerRect.IsEmpty) + { + Rectangle r = new Rectangle(outerRect.X, outerRect.Y, CONTAINER_WIDTH, CONTAINER_HEIGHT); + region = new Region(r); + } + else + { + region = new Region(new Rectangle(0, 0, 0, 0)); + } + } + return region; + } + } + + private struct HitTestInfo + { + public readonly int hitTest; + public readonly SelectionUIItem selectionUIHit; + public readonly bool containerSelector; + + public HitTestInfo(int hitTest, SelectionUIItem selectionUIHit) + { + this.hitTest = hitTest; + this.selectionUIHit = selectionUIHit; + containerSelector = false; + } + + public HitTestInfo(int hitTest, SelectionUIItem selectionUIHit, bool containerSelector) + { + this.hitTest = hitTest; + this.selectionUIHit = selectionUIHit; + this.containerSelector = containerSelector; + } + + // Standard 'catch all - rethrow critical' exception pattern + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + public override bool Equals(object obj) + { + try + { + HitTestInfo hi = (HitTestInfo)obj; + return hitTest == hi.hitTest && selectionUIHit == hi.selectionUIHit && containerSelector == hi.containerSelector; + } + catch (Exception ex) + { + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + return false; + } + + public static bool operator ==(HitTestInfo left, HitTestInfo right) + { + return (left.hitTest == right.hitTest && left.selectionUIHit == right.selectionUIHit && left.containerSelector == right.containerSelector); + } + + public static bool operator !=(HitTestInfo left, HitTestInfo right) => !(left == right); + + public override int GetHashCode() + { + int hash = hitTest | selectionUIHit.GetHashCode(); + if (containerSelector) + { + hash |= 0x10000; + } + return hash; + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardCommandToolStripMenuItem.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardCommandToolStripMenuItem.cs new file mode 100644 index 00000000000..f45ad62ee8b --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardCommandToolStripMenuItem.cs @@ -0,0 +1,123 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel.Design; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; + +namespace System.Windows.Forms.Design +{ + /// + /// Associates standard command with ToolStripMenuItem. + /// + internal class StandardCommandToolStripMenuItem : ToolStripMenuItem + { + private bool _cachedImage = false; + private Image _image = null; + private readonly CommandID _menuID; + private IMenuCommandService _menuCommandService; + private readonly IServiceProvider _serviceProvider; + private readonly string _name; + private readonly MenuCommand _menuCommand; + + // Ok to call MenuService.FindComand to find the menuCommand mapping to the appropriated menuID. + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + public StandardCommandToolStripMenuItem(CommandID menuID, string text, string imageName, IServiceProvider serviceProvider) + { + _menuID = menuID; + _serviceProvider = serviceProvider; + // Findcommand can throw; so we need to catch and disable the command. + try + { + _menuCommand = MenuService.FindCommand(menuID); + } + catch + { + Enabled = false; + } + + Text = text; + _name = imageName; + + RefreshItem(); + } + + public void RefreshItem() + { + if (_menuCommand != null) + { + Visible = _menuCommand.Visible; + Enabled = _menuCommand.Enabled; + Checked = _menuCommand.Checked; + } + } + + /// + /// Retrieves the menu editor service, which we cache for speed. + /// + public IMenuCommandService MenuService + { + get + { + if (_menuCommandService == null) + { + _menuCommandService = (IMenuCommandService)_serviceProvider.GetService(typeof(IMenuCommandService)); + } + return _menuCommandService; + } + } + + public override Image Image + { + // Standard 'catch all - rethrow critical' exception pattern + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + get + { + // Defer loading the image until we're sure we need it + if (!_cachedImage) + { + _cachedImage = true; + try + { + if (_name != null) + { + _image = new Bitmap(BitmapSelector.GetResourceStream(typeof(ToolStripMenuItem), _name + ".bmp")); + } + ImageTransparentColor = Color.Magenta; + } + catch (Exception ex) + { + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + } + return _image; + } + set + { + _image = value; + _cachedImage = true; + } + } + + protected override void OnClick(System.EventArgs e) + { + if (_menuCommand != null) + { + _menuCommand.Invoke(); + } + else if (MenuService != null) + { + if (MenuService.GlobalInvoke(_menuID)) + { + return; + } + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardGroups.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardGroups.cs new file mode 100644 index 00000000000..90e48fcb4f1 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardGroups.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; + +namespace System.Windows.Forms.Design +{ + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + internal class StandardGroups + { + public const string Code = "Code"; + public const string ZORder = "ZOrder"; + public const string Grid = "Grid"; + public const string Lock = "Lock"; + public const string Verbs = "Verbs"; + public const string Custom = "Custom"; + public const string Selection = "Selection"; + public const string Edit = "Edit"; + public const string Properties = "Properties"; + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardMenuStripVerb.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardMenuStripVerb.cs new file mode 100644 index 00000000000..a46ae2338f7 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardMenuStripVerb.cs @@ -0,0 +1,525 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.ComponentModel.Design; +using System.ComponentModel.Design.Serialization; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Globalization; + +namespace System.Windows.Forms.Design +{ + /// + /// Internal class to provide 'Insert Standard Items" verb for ToolStrips & MenuStrips. + /// + internal class StandardMenuStripVerb + { + private readonly ToolStripDesigner _designer; + private readonly IDesignerHost _host; + private readonly IComponentChangeService _componentChangeSvc; + private readonly IServiceProvider _provider; + + /// + /// Create one of these things... + /// + internal StandardMenuStripVerb(ToolStripDesigner designer) + { + Debug.Assert(designer != null, "Can't have a StandardMenuStripVerb without an associated designer"); + _designer = designer; + _provider = designer.Component.Site; + _host = (IDesignerHost)_provider.GetService(typeof(IDesignerHost)); + _componentChangeSvc = (IComponentChangeService)_provider.GetService(typeof(IComponentChangeService)); + } + + /// + /// When the verb is invoked, use all the stuff above to show the dialog, etc. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public void InsertItems() + { + DesignerActionUIService actionUIService = (DesignerActionUIService)_host.GetService(typeof(DesignerActionUIService)); + if (actionUIService != null) + { + actionUIService.HideUI(_designer.Component); + } + Cursor current = Cursor.Current; + try + { + Cursor.Current = Cursors.WaitCursor; + if (_designer.Component is MenuStrip) + { + CreateStandardMenuStrip(_host, (MenuStrip)_designer.Component); + } + else + { + CreateStandardToolStrip(_host, (ToolStrip)_designer.Component); + } + } + finally + { + Cursor.Current = current; + } + } + + /// + /// Here is where all the fun stuff starts. We create the structure and apply the naming here. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + private void CreateStandardMenuStrip(System.ComponentModel.Design.IDesignerHost host, MenuStrip tool) + { + // build the static menu items structure. + string[][] menuItemNames = new string[][]{ + new string[]{SR.StandardMenuFile, SR.StandardMenuNew, SR.StandardMenuOpen, "-", SR.StandardMenuSave, SR.StandardMenuSaveAs, "-", SR.StandardMenuPrint, SR.StandardMenuPrintPreview, "-", SR.StandardMenuExit}, + new string[]{SR.StandardMenuEdit, SR.StandardMenuUndo, SR.StandardMenuRedo, "-", SR.StandardMenuCut, SR.StandardMenuCopy, SR.StandardMenuPaste, "-", SR.StandardMenuSelectAll}, + new string[]{SR.StandardMenuTools, SR.StandardMenuCustomize, SR.StandardMenuOptions}, + new string[]{SR.StandardMenuHelp, SR.StandardMenuContents, SR.StandardMenuIndex, SR.StandardMenuSearch, "-", SR.StandardMenuAbout } }; + + // build the static menu items image list that maps one-one with above menuItems structure. this is required so that the in LOCALIZED build we dont use the Localized item string. + string[][] menuItemImageNames = new string[][]{ + new string[]{"","new", "open", "-", "save", "", "-", "print", "printPreview", "-", ""}, + new string[]{"", "", "", "-", "cut", "copy", "paste", "-", ""}, + new string[]{"", "", ""}, + new string[]{"", "", "", "", "-", ""}}; + + Keys[][] menuItemShortcuts = new Keys[][]{ + new Keys[]{/*File*/Keys.None, /*New*/Keys.Control | Keys.N, /*Open*/Keys.Control | Keys.O, /*Separator*/ Keys.None, /*Save*/ Keys.Control | Keys.S, /*SaveAs*/Keys.None, Keys.None, /*Print*/ Keys.Control | Keys.P, /*PrintPreview*/ Keys.None, /*Separator*/Keys.None, /*Exit*/ Keys.None}, + new Keys[]{/*Edit*/Keys.None, /*Undo*/Keys.Control | Keys.Z, /*Redo*/Keys.Control | Keys.Y, /*Separator*/Keys.None, /*Cut*/ Keys.Control | Keys.X, /*Copy*/ Keys.Control | Keys.C, /*Paste*/Keys.Control | Keys.V, /*Separator*/ Keys.None, /*SelectAll*/Keys.None}, + new Keys[]{/*Tools*/Keys.None, /*Customize*/Keys.None, /*Options*/Keys.None}, + new Keys[]{/*Help*/Keys.None, /*Contents*/Keys.None, /*Index*/Keys.None, /*Search*/Keys.None,/*Separator*/Keys.None , /*About*/Keys.None}}; + + Debug.Assert(host != null, "can't create standard menu without designer _host."); + if (host == null) + { + return; + } + tool.SuspendLayout(); + ToolStripDesigner.s_autoAddNewItems = false; + // create a transaction so this happens as an atomic unit. + DesignerTransaction createMenu = _host.CreateTransaction(SR.StandardMenuCreateDesc); + try + { + INameCreationService nameCreationService = (INameCreationService)_provider.GetService(typeof(INameCreationService)); + string defaultName = "standardMainMenuStrip"; + string name = defaultName; + int index = 1; + + if (host != null) + { + while (_host.Container.Components[name] != null) + { + name = defaultName + (index++).ToString(CultureInfo.InvariantCulture); + } + } + + // now build the menu items themselves. + for (int j = 0; j < menuItemNames.Length; j++) + { + string[] menuArray = menuItemNames[j]; + ToolStripMenuItem rootItem = null; + for (int i = 0; i < menuArray.Length; i++) + { + name = null; + // for separators, just use the default name. Otherwise, remove any non-characters and get the name from the text. + string itemText = menuArray[i]; + name = NameFromText(itemText, typeof(ToolStripMenuItem), nameCreationService, true); + ToolStripItem item = null; + if (name.Contains("Separator")) + { + // create the componennt. + item = (ToolStripSeparator)_host.CreateComponent(typeof(ToolStripSeparator), name); + IDesigner designer = _host.GetDesigner(item); + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + item.Text = itemText; + } + else + { + // create the componennt. + item = (ToolStripMenuItem)_host.CreateComponent(typeof(ToolStripMenuItem), name); + IDesigner designer = _host.GetDesigner(item); + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + item.Text = itemText; + Keys shortcut = menuItemShortcuts[j][i]; + if ((item is ToolStripMenuItem) && shortcut != Keys.None) + { + if (!ToolStripManager.IsShortcutDefined(shortcut) && ToolStripManager.IsValidShortcut(shortcut)) + { + ((ToolStripMenuItem)item).ShortcutKeys = shortcut; + } + } + Bitmap image = null; + try + { + image = GetImage(menuItemImageNames[j][i]); + } + catch + { + // eat the exception.. as you may not find image for all MenuItems. + } + if (image != null) + { + PropertyDescriptor imageProperty = TypeDescriptor.GetProperties(item)["Image"]; + Debug.Assert(imageProperty != null, "Could not find 'Image' property in ToolStripItem."); + if (imageProperty != null) + { + imageProperty.SetValue(item, image); + } + item.ImageTransparentColor = Color.Magenta; + } + } + + // the first item in each array is the root item. + if (i == 0) + { + rootItem = (ToolStripMenuItem)item; + rootItem.DropDown.SuspendLayout(); + } + else + { + rootItem.DropDownItems.Add(item); + } + //If Last SubItem Added the Raise the Events + if (i == menuArray.Length - 1) + { + // member is OK to be null... + MemberDescriptor member = TypeDescriptor.GetProperties(rootItem)["DropDownItems"]; + _componentChangeSvc.OnComponentChanging(rootItem, member); + _componentChangeSvc.OnComponentChanged(rootItem, member, null, null); + } + } + + // finally, add it to the MainMenu. + rootItem.DropDown.ResumeLayout(false); + tool.Items.Add(rootItem); + //If Last SubItem Added the Raise the Events + if (j == menuItemNames.Length - 1) + { + // member is OK to be null... + MemberDescriptor topMember = TypeDescriptor.GetProperties(tool)["Items"]; + _componentChangeSvc.OnComponentChanging(tool, topMember); + _componentChangeSvc.OnComponentChanged(tool, topMember, null, null); + } + } + } + catch (Exception e) + { + if (e is InvalidOperationException) + { + IUIService uiService = (IUIService)_provider.GetService(typeof(IUIService)); + uiService.ShowError(e.Message); + } + if (createMenu != null) + { + createMenu.Cancel(); + createMenu = null; + } + } + finally + { + ToolStripDesigner.s_autoAddNewItems = true; + if (createMenu != null) + { + createMenu.Commit(); + createMenu = null; + } + tool.ResumeLayout(); + // Select the Main Menu... + ISelectionService selSvc = (ISelectionService)_provider.GetService(typeof(ISelectionService)); + if (selSvc != null) + { + selSvc.SetSelectedComponents(new object[] { _designer.Component }); + } + //Refresh the Glyph + DesignerActionUIService actionUIService = (DesignerActionUIService)_provider.GetService(typeof(DesignerActionUIService)); + if (actionUIService != null) + { + actionUIService.Refresh(_designer.Component); + } + // this will invalidate the Selection Glyphs. + SelectionManager selMgr = (SelectionManager)_provider.GetService(typeof(SelectionManager)); + selMgr.Refresh(); + } + } + + /// + /// Here is where all the fun stuff starts. We create the structure and apply the naming here. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + private void CreateStandardToolStrip(IDesignerHost host, ToolStrip tool) + { + + // build the static menu items structure. + // + string[] menuItemNames = new string[] { SR.StandardMenuNew, SR.StandardMenuOpen, SR.StandardMenuSave, SR.StandardMenuPrint, "-", SR.StandardToolCut, SR.StandardMenuCopy, SR.StandardMenuPaste, "-", SR.StandardToolHelp }; + + // build a image list mapping one-one the above menuItems list... this is required so that the in LOCALIZED build we dont use the Localized item string. + string[] menuItemImageNames = new string[] { "new", "open", "save", "print", "-", "cut", "copy", "paste", "-", "help" }; + Debug.Assert(host != null, "can't create standard menu without designer _host."); + + if (host == null) + { + return; + } + + tool.SuspendLayout(); + ToolStripDesigner.s_autoAddNewItems = false; + // create a transaction so this happens as an atomic unit. + DesignerTransaction createMenu = _host.CreateTransaction(SR.StandardMenuCreateDesc); + try + { + INameCreationService nameCreationService = (INameCreationService)_provider.GetService(typeof(INameCreationService)); + string defaultName = "standardMainToolStrip"; + string name = defaultName; + int index = 1; + if (host != null) + { + while (_host.Container.Components[name] != null) + { + name = defaultName + (index++).ToString(CultureInfo.InvariantCulture); + } + } + + //keep an index in the MenuItemImageNames .. so that mapping is maintained. + int menuItemImageNamesCount = 0; + // now build the menu items themselves. + foreach (string itemText in menuItemNames) + { + name = null; + // for separators, just use the default name. Otherwise, remove any non-characters and get the name from the text. + defaultName = "ToolStripButton"; + name = NameFromText(itemText, typeof(ToolStripButton), nameCreationService, true); + ToolStripItem item = null; + if (name.Contains("Separator")) + { + // create the componennt. + item = (ToolStripSeparator)_host.CreateComponent(typeof(ToolStripSeparator), name); + IDesigner designer = _host.GetDesigner(item); + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + } + else + { + // create the component. + item = (ToolStripButton)_host.CreateComponent(typeof(ToolStripButton), name); + IDesigner designer = _host.GetDesigner(item); + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + + PropertyDescriptor displayStyleProperty = TypeDescriptor.GetProperties(item)["DisplayStyle"]; + Debug.Assert(displayStyleProperty != null, "Could not find 'Text' property in ToolStripItem."); + if (displayStyleProperty != null) + { + displayStyleProperty.SetValue(item, ToolStripItemDisplayStyle.Image); + } + + PropertyDescriptor textProperty = TypeDescriptor.GetProperties(item)["Text"]; + Debug.Assert(textProperty != null, "Could not find 'Text' property in ToolStripItem."); + if (textProperty != null) + { + textProperty.SetValue(item, itemText); + } + + Bitmap image = null; + try + { + image = GetImage(menuItemImageNames[menuItemImageNamesCount]); + } + catch + { + // eat the exception.. as you may not find image for all MenuItems. + } + if (image != null) + { + PropertyDescriptor imageProperty = TypeDescriptor.GetProperties(item)["Image"]; + Debug.Assert(imageProperty != null, "Could not find 'Image' property in ToolStripItem."); + if (imageProperty != null) + { + imageProperty.SetValue(item, image); + } + item.ImageTransparentColor = Color.Magenta; + } + } + tool.Items.Add(item); + //increment the counter... + menuItemImageNamesCount++; + } + // finally, add it to the Main ToolStrip. + MemberDescriptor topMember = TypeDescriptor.GetProperties(tool)["Items"]; + _componentChangeSvc.OnComponentChanging(tool, topMember); + _componentChangeSvc.OnComponentChanged(tool, topMember, null, null); + } + catch (Exception e) + { + if (e is InvalidOperationException) + { + IUIService uiService = (IUIService)_provider.GetService(typeof(IUIService)); + uiService.ShowError(e.Message); + } + if (createMenu != null) + { + createMenu.Cancel(); + createMenu = null; + } + } + finally + { + //Reset the AutoAdd state + ToolStripDesigner.s_autoAddNewItems = true; + if (createMenu != null) + { + createMenu.Commit(); + createMenu = null; + } + tool.ResumeLayout(); + // Select the Main Menu... + ISelectionService selSvc = (ISelectionService)_provider.GetService(typeof(ISelectionService)); + if (selSvc != null) + { + selSvc.SetSelectedComponents(new object[] { _designer.Component }); + } + + //Refresh the Glyph + DesignerActionUIService actionUIService = (DesignerActionUIService)_provider.GetService(typeof(DesignerActionUIService)); + if (actionUIService != null) + { + actionUIService.Refresh(_designer.Component); + } + // this will invalidate the Selection Glyphs. + SelectionManager selMgr = (SelectionManager)_provider.GetService(typeof(SelectionManager)); + selMgr.Refresh(); + } + + } + + /// + /// Helper Function to get Images from types. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private Bitmap GetImage(string name) + { + Bitmap image = null; + if (name.StartsWith("new")) + { + image = new Bitmap(typeof(ToolStripMenuItem), "new.bmp"); + } + else if (name.StartsWith("open")) + { + image = new Bitmap(typeof(ToolStripMenuItem), "open.bmp"); + } + else if (name.StartsWith("save")) + { + image = new Bitmap(typeof(ToolStripMenuItem), "save.bmp"); + } + else if (name.StartsWith("printPreview")) + { + image = new Bitmap(typeof(ToolStripMenuItem), "printPreview.bmp"); + } + else if (name.StartsWith("print")) + { + image = new Bitmap(typeof(ToolStripMenuItem), "print.bmp"); + } + else if (name.StartsWith("cut")) + { + image = new Bitmap(typeof(ToolStripMenuItem), "cut.bmp"); + } + else if (name.StartsWith("copy")) + { + image = new Bitmap(typeof(ToolStripMenuItem), "copy.bmp"); + } + else if (name.StartsWith("paste")) + { + image = new Bitmap(typeof(ToolStripMenuItem), "paste.bmp"); + } + else if (name.StartsWith("help")) + { + image = new Bitmap(typeof(ToolStripMenuItem), "help.bmp"); + } + return image; + } + + /// + /// Computes a name from a text label by removing all spaces and non-alphanumeric characters. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private string NameFromText(string text, Type itemType, INameCreationService nameCreationService, bool adjustCapitalization) + { + string baseName; + // for separators, name them ToolStripSeparator... + if (text == "-") + { + baseName = "toolStripSeparator"; + } + else + { + string nameSuffix = itemType.Name; + // remove all the non letter and number characters. Append length of "MenuItem" + Text.StringBuilder name = new Text.StringBuilder(text.Length + nameSuffix.Length); + bool firstCharSeen = false; + for (int i = 0; i < text.Length; i++) + { + char c = text[i]; + if (char.IsLetterOrDigit(c)) + { + if (!firstCharSeen) + { + c = char.ToLower(c, CultureInfo.CurrentCulture); + firstCharSeen = true; + } + name.Append(c); + } + } + name.Append(nameSuffix); + baseName = name.ToString(); + if (adjustCapitalization) + { + string nameOfRandomItem = ToolStripDesigner.NameFromText(null, typeof(ToolStripMenuItem), _designer.Component.Site); + if (!string.IsNullOrEmpty(nameOfRandomItem) && char.IsUpper(nameOfRandomItem[0])) + { + baseName = char.ToUpper(baseName[0], CultureInfo.InvariantCulture) + baseName.Substring(1); + } + } + } + + // see if this name matches another one in the container.. + object existingComponent = _host.Container.Components[baseName]; + if (existingComponent == null) + { + if (!nameCreationService.IsValidName(baseName)) + { + // we don't have a name collision but this still isn't a valid name...something is wrong and we can't make a valid identifier out of this so bail. + return nameCreationService.CreateName(_host.Container, itemType); + } + else + { + return baseName; + } + } + else + { + // start appending numbers. + string newName = baseName; + for (int indexer = 1; !nameCreationService.IsValidName(newName); indexer++) + { + newName = baseName + indexer.ToString(CultureInfo.InvariantCulture); + } + return newName; + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/TemplateNodeCustomMenuItemCollection.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/TemplateNodeCustomMenuItemCollection.cs new file mode 100644 index 00000000000..42edc647079 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/TemplateNodeCustomMenuItemCollection.cs @@ -0,0 +1,167 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; + +namespace System.Windows.Forms.Design +{ + /// + /// Custom ContextMenu section for ToolStripMenuItems. + /// + internal class TemplateNodeCustomMenuItemCollection : CustomMenuItemCollection + { + private readonly ToolStripItem _currentItem; + private readonly IServiceProvider _serviceProvider; + private ToolStripMenuItem _insertToolStripMenuItem; + + public TemplateNodeCustomMenuItemCollection(IServiceProvider provider, Component currentItem) : base() + { + _serviceProvider = provider; + _currentItem = currentItem as ToolStripItem; + PopulateList(); + } + + /// + /// Immediate parent - can be ToolStrip if the Item is on the toplevel + /// + private ToolStrip ParentTool + { + get => _currentItem.Owner; + } + + private void PopulateList() + { + _insertToolStripMenuItem = new ToolStripMenuItem + { + Text = SR.ToolStripItemContextMenuInsert, + DropDown = ToolStripDesignerUtils.GetNewItemDropDown(ParentTool, _currentItem, new EventHandler(AddNewItemClick), false, _serviceProvider, true) + }; + Add(_insertToolStripMenuItem); + } + + private void AddNewItemClick(object sender, EventArgs e) + { + ItemTypeToolStripMenuItem senderItem = (ItemTypeToolStripMenuItem)sender; + Type t = senderItem.ItemType; + // we are inserting a new item.. + InsertItem(t); + } + + private void InsertItem(Type t) + { + InsertToolStripItem(t); + } + + /// + /// Insert Item into ToolStrip. + /// + // Standard 'catch all - rethrow critical' exception pattern + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + private void InsertToolStripItem(Type t) + { + IDesignerHost designerHost = (IDesignerHost)_serviceProvider.GetService(typeof(IDesignerHost)); + Debug.Assert(designerHost != null, "Why didn't we get a designer host?"); + ToolStrip parent = ParentTool; + int dummyIndex = parent.Items.IndexOf(_currentItem); + DesignerTransaction newItemTransaction = designerHost.CreateTransaction(SR.ToolStripAddingItem); + try + { + // turn off Adding/Added events listened to by the ToolStripDesigner... + ToolStripDesigner.s_autoAddNewItems = false; + // the code in ComponentAdded will actually get the add done. + IComponent component = designerHost.CreateComponent(t); + IDesigner designer = designerHost.GetDesigner(component); + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + + //Set the Image property and DisplayStyle... + if (component is ToolStripButton || component is ToolStripSplitButton || component is ToolStripDropDownButton) + { + Image image = null; + try + { + image = new Bitmap(typeof(ToolStripButton), "blank.bmp"); + } + catch (Exception e) + { + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + } + + PropertyDescriptor imageProperty = TypeDescriptor.GetProperties(component)["Image"]; + Debug.Assert(imageProperty != null, "Could not find 'Image' property in ToolStripItem."); + if (imageProperty != null && image != null) + { + imageProperty.SetValue(component, image); + } + + PropertyDescriptor dispProperty = TypeDescriptor.GetProperties(component)["DisplayStyle"]; + Debug.Assert(dispProperty != null, "Could not find 'DisplayStyle' property in ToolStripItem."); + if (dispProperty != null) + { + dispProperty.SetValue(component, ToolStripItemDisplayStyle.Image); + } + + PropertyDescriptor imageTransProperty = TypeDescriptor.GetProperties(component)["ImageTransparentColor"]; + Debug.Assert(imageTransProperty != null, "Could not find 'DisplayStyle' property in ToolStripItem."); + if (imageTransProperty != null) + { + imageTransProperty.SetValue(component, Color.Magenta); + } + } + Debug.Assert(dummyIndex != -1, "Why is the index of the Item negative?"); + parent.Items.Insert(dummyIndex, (ToolStripItem)component); + // set the selection to our new item.. since we destroyed Original component.. we have to ask SelectionServive from new Component + ISelectionService selSvc = (ISelectionService)_serviceProvider.GetService(typeof(ISelectionService)); + if (selSvc != null) + { + selSvc.SetSelectedComponents(new object[] { component }, SelectionTypes.Replace); + } + } + catch (Exception ex) + { + if (newItemTransaction != null) + { + newItemTransaction.Cancel(); + newItemTransaction = null; + } + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + finally + { + if (newItemTransaction != null) + { + newItemTransaction.Commit(); + newItemTransaction = null; + } + // turn off Adding/Added events listened to by the ToolStripDesigner... + ToolStripDesigner.s_autoAddNewItems = true; + // Add the glyphs if the parent is DropDown. + if (parent is ToolStripDropDown parentDropDown && parentDropDown.Visible) + { + if (parentDropDown.OwnerItem is ToolStripDropDownItem ownerItem) + { + ToolStripMenuItemDesigner itemDesigner = designerHost.GetDesigner(ownerItem) as ToolStripMenuItemDesigner; + if (itemDesigner != null) + { + itemDesigner.ResetGlyphs(ownerItem); + } + } + } + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/TemplateNodeSelectionState.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/TemplateNodeSelectionState.cs new file mode 100644 index 00000000000..82f5b104805 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/TemplateNodeSelectionState.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Design +{ + internal enum TemplateNodeSelectionState + { + None = 0, + TemplateNodeSelected = 1, + SplitButtonSelected = 2, + DropDownSelected = 3, + MouseOverLabel = 4, + MouseOverHotRegion = 5, + HotRegionSelected = 6 + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripActionList.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripActionList.cs new file mode 100644 index 00000000000..ab52e75b0c9 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripActionList.cs @@ -0,0 +1,196 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace System.Windows.Forms.Design +{ + internal class ToolStripActionList : DesignerActionList + { + private readonly ToolStrip _toolStrip; + private bool _autoShow = false; + private readonly ToolStripDesigner _designer; + + private ChangeToolStripParentVerb _changeParentVerb = null; + private StandardMenuStripVerb _standardItemsVerb = null; + + public ToolStripActionList(ToolStripDesigner designer) : base(designer.Component) + { + _toolStrip = (ToolStrip)designer.Component; + this._designer = designer; + + _changeParentVerb = new ChangeToolStripParentVerb(string.Format(SR.ToolStripDesignerEmbedVerb), designer); + if (!(_toolStrip is StatusStrip)) + { + _standardItemsVerb = new StandardMenuStripVerb(designer); + } + } + + /// + /// False if were inherited and can't be modified. + /// + private bool CanAddItems + { + get + { + // Make sure the component is not being inherited -- we can't delete these! + InheritanceAttribute ia = (InheritanceAttribute)TypeDescriptor.GetAttributes(_toolStrip)[typeof(InheritanceAttribute)]; + if (ia == null || ia.InheritanceLevel == InheritanceLevel.NotInherited) + { + return true; + } + return false; + } + } + + private bool IsReadOnly + { + get + { + // Make sure the component is not being inherited -- we can't delete these! + InheritanceAttribute ia = (InheritanceAttribute)TypeDescriptor.GetAttributes(_toolStrip)[typeof(InheritanceAttribute)]; + if (ia == null || ia.InheritanceLevel == InheritanceLevel.InheritedReadOnly) + { + return true; + } + return false; + } + } + + //helper function to get the property on the actual Control + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private object GetProperty(string propertyName) + { + PropertyDescriptor getProperty = TypeDescriptor.GetProperties(_toolStrip)[propertyName]; + Debug.Assert(getProperty != null, "Could not find given property in control."); + if (getProperty != null) + { + return getProperty.GetValue(_toolStrip); + } + return null; + } + + //helper function to change the property on the actual Control + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private void ChangeProperty(string propertyName, object value) + { + PropertyDescriptor changingProperty = TypeDescriptor.GetProperties(_toolStrip)[propertyName]; + Debug.Assert(changingProperty != null, "Could not find given property in control."); + if (changingProperty != null) + { + changingProperty.SetValue(_toolStrip, value); + } + } + + /// + /// Controls whether the Chrome is Automatically shown on selection + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public override bool AutoShow + { + get => _autoShow; + set + { + if (_autoShow != value) + { + _autoShow = value; + } + } + } + + public DockStyle Dock + { + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + get => (DockStyle)GetProperty("Dock"); + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + set + { + if (value != Dock) + { + ChangeProperty("Dock", (object)value); + } + } + } + + public ToolStripRenderMode RenderMode + { + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + get => (ToolStripRenderMode)GetProperty("RenderMode"); + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + set + { + if (value != RenderMode) + { + ChangeProperty("RenderMode", (object)value); + } + } + } + + public ToolStripGripStyle GripStyle + { + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + get => (ToolStripGripStyle)GetProperty("GripStyle"); + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + set + { + if (value != GripStyle) + { + ChangeProperty("GripStyle", (object)value); + } + } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private void InvokeEmbedVerb() + { + // Hide the Panel... + DesignerActionUIService actionUIService = (DesignerActionUIService)_toolStrip.Site.GetService(typeof(DesignerActionUIService)); + if (actionUIService != null) + { + actionUIService.HideUI(_toolStrip); + } + _changeParentVerb.ChangeParent(); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private void InvokeInsertStandardItemsVerb() + { + _standardItemsVerb.InsertItems(); + } + + /// + /// The Main method to group the ActionItems and pass it to the Panel. + /// + public override DesignerActionItemCollection GetSortedActionItems() + { + DesignerActionItemCollection items = new DesignerActionItemCollection(); + if (!IsReadOnly) + { + items.Add(new DesignerActionMethodItem(this, "InvokeEmbedVerb", SR.ToolStripDesignerEmbedVerb, "", SR.ToolStripDesignerEmbedVerbDesc, true)); + } + + if (CanAddItems) + { + if (!(_toolStrip is StatusStrip)) + { + items.Add(new DesignerActionMethodItem(this, "InvokeInsertStandardItemsVerb", SR.ToolStripDesignerStandardItemsVerb, "", SR.ToolStripDesignerStandardItemsVerbDesc, true)); + } + items.Add(new DesignerActionPropertyItem("RenderMode", SR.ToolStripActionList_RenderMode, SR.ToolStripActionList_Layout, SR.ToolStripActionList_RenderModeDesc)); + } + + if (!(_toolStrip.Parent is ToolStripPanel)) + { + items.Add(new DesignerActionPropertyItem("Dock", SR.ToolStripActionList_Dock, SR.ToolStripActionList_Layout, SR.ToolStripActionList_DockDesc)); + } + if (!(_toolStrip is StatusStrip)) + { + items.Add(new DesignerActionPropertyItem("GripStyle", SR.ToolStripActionList_GripStyle, SR.ToolStripActionList_Layout, SR.ToolStripActionList_GripStyleDesc)); + } + return items; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripAdornerWindowService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripAdornerWindowService.cs new file mode 100644 index 00000000000..08a9a983b3e --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripAdornerWindowService.cs @@ -0,0 +1,310 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// Transparent Window to parent the DropDowns. + /// + internal sealed class ToolStripAdornerWindowService : IDisposable + { + private readonly IServiceProvider _serviceProvider; //standard service provider + private readonly ToolStripAdornerWindow _toolStripAdornerWindow; //the transparent window all glyphs are drawn to + private BehaviorService _behaviorService; + private Adorner _dropDownAdorner; + private ArrayList _dropDownCollection; + private readonly IOverlayService _overlayService; + + /// + /// This constructor is called from DocumentDesigner's Initialize method. + /// + internal ToolStripAdornerWindowService(IServiceProvider serviceProvider, Control windowFrame) + { + _serviceProvider = serviceProvider; + //create the AdornerWindow + _toolStripAdornerWindow = new ToolStripAdornerWindow(windowFrame); + _behaviorService = (BehaviorService)serviceProvider.GetService(typeof(BehaviorService)); + int indexToInsert = _behaviorService.AdornerWindowIndex; + + //use the adornerWindow as an overlay + _overlayService = (IOverlayService)serviceProvider.GetService(typeof(IOverlayService)); + if (_overlayService != null) + { + _overlayService.InsertOverlay(_toolStripAdornerWindow, indexToInsert); + } + + _dropDownAdorner = new Adorner(); + int count = _behaviorService.Adorners.Count; + + // Why this is NEEDED ? To Add the Adorner at proper index in the AdornerCollection for the BehaviorService. So that the DesignerActionGlyph always stays on the Top. + if (count > 1) + { + _behaviorService.Adorners.Insert(count - 1, _dropDownAdorner); + } + } + + /// + /// Returns the actual Control that represents the transparent AdornerWindow. + /// + internal Control ToolStripAdornerWindowControl + { + get => _toolStripAdornerWindow; + } + + /// + /// Creates and returns a Graphics object for the AdornerWindow + /// + public Graphics ToolStripAdornerWindowGraphics + { + get => _toolStripAdornerWindow.CreateGraphics(); + } + + internal Adorner DropDownAdorner + { + get => _dropDownAdorner; + } + + /// + /// Disposes the behavior service. + /// + [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] + public void Dispose() + { + if (_overlayService != null) + { + _overlayService.RemoveOverlay(_toolStripAdornerWindow); + } + _toolStripAdornerWindow.Dispose(); + if (_behaviorService != null) + { + _behaviorService.Adorners.Remove(_dropDownAdorner); + _behaviorService = null; + } + if (_dropDownAdorner != null) + { + _dropDownAdorner.Glyphs.Clear(); + _dropDownAdorner = null; + } + } + + /// + /// Translates a point in the AdornerWindow to screen coords. + /// + public Point AdornerWindowPointToScreen(Point p) + { + NativeMethods.POINT offset = new NativeMethods.POINT(p.X, p.Y); + NativeMethods.MapWindowPoints(_toolStripAdornerWindow.Handle, IntPtr.Zero, offset, 1); + return new Point(offset.x, offset.y); + } + + /// + /// Gets the location (upper-left corner) of the AdornerWindow in screen coords. + /// + public Point AdornerWindowToScreen() + { + Point origin = new Point(0, 0); + return AdornerWindowPointToScreen(origin); + } + + /// + /// Returns the location of a Control translated to AdornerWidnow coords. + /// + public Point ControlToAdornerWindow(Control c) + { + if (c.Parent == null) + { + return Point.Empty; + } + NativeMethods.POINT pt = new NativeMethods.POINT + { + x = c.Left, + y = c.Top + }; + NativeMethods.MapWindowPoints(c.Parent.Handle, _toolStripAdornerWindow.Handle, pt, 1); + return new Point(pt.x, pt.y); + } + + /// + /// Invalidates the BehaviorService's AdornerWindow. This will force a refesh of all Adorners and, in turn, all Glyphs. + /// + public void Invalidate() + { + _toolStripAdornerWindow.InvalidateAdornerWindow(); + } + + /// + /// Invalidates the BehaviorService's AdornerWindow. This will force a refesh of all Adorners and, in turn, all Glyphs. + /// + public void Invalidate(Rectangle rect) + { + _toolStripAdornerWindow.InvalidateAdornerWindow(rect); + } + + /// + /// Invalidates the BehaviorService's AdornerWindow. This will force a refesh of all Adorners and, in turn, all Glyphs. + /// + public void Invalidate(Region r) + { + _toolStripAdornerWindow.InvalidateAdornerWindow(r); + } + + internal ArrayList DropDowns + { + get => _dropDownCollection; + set + { + if (_dropDownCollection == null) + { + _dropDownCollection = new ArrayList(); + } + } + + } + + /// + /// ControlDesigner calls this internal method in response to a WmPaint. We need to know when a ControlDesigner paints - 'cause we will need to re-paint any glyphs above of this Control. + /// + internal void ProcessPaintMessage(Rectangle paintRect) + { + // Note, we don't call BehSvc.Invalidate because this will just cause the messages to recurse. Instead, invalidating this adornerWindow will just cause a "propagatePaint" and draw the glyphs. + _toolStripAdornerWindow.Invalidate(paintRect); + } + + /// + /// The AdornerWindow is a transparent window that resides ontop of the Designer's Frame. This window is used by the ToolStripAdornerWindowService to parent the MenuItem DropDowns. + /// + private class ToolStripAdornerWindow : Control + { + private Control _designerFrame; //the designer's frame + + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + internal ToolStripAdornerWindow(Control designerFrame) + { + _designerFrame = designerFrame; + Dock = DockStyle.Fill; + AllowDrop = true; + Text = "ToolStripAdornerWindow"; + SetStyle(ControlStyles.Opaque, true); + } + + /// + /// The key here is to set the appropriate TransparetWindow style. + /// + protected override CreateParams CreateParams + { + get + { + CreateParams cp = base.CreateParams; + cp.Style &= ~(NativeMethods.WS_CLIPCHILDREN | NativeMethods.WS_CLIPSIBLINGS); + cp.ExStyle |= 0x00000020/*WS_EX_TRANSPARENT*/; + return cp; + } + } + + /// + /// We'll use CreateHandle as our notification for creating our mouse hooker. + /// + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + } + + /// + /// Unhook and null out our mouseHook. + /// + protected override void OnHandleDestroyed(EventArgs e) + { + base.OnHandleDestroyed(e); + } + + /// + /// Null out our mouseHook and unhook any events. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_designerFrame != null) + { + _designerFrame = null; + } + + } + base.Dispose(disposing); + } + + /// + /// Returns true if the DesignerFrame is created & not being disposed. + /// + private bool DesignerFrameValid + { + get + { + if (_designerFrame == null || _designerFrame.IsDisposed || !_designerFrame.IsHandleCreated) + { + return false; + } + return true; + } + } + + /// + /// Invalidates the transparent AdornerWindow by asking the Designer Frame beneath it to invalidate. Note the they use of the .Update() call for perf. purposes. + /// + internal void InvalidateAdornerWindow() + { + if (DesignerFrameValid) + { + _designerFrame.Invalidate(true); + _designerFrame.Update(); + } + } + + /// + /// Invalidates the transparent AdornerWindow by asking the Designer Frame beneath it to invalidate. Note the they use of the .Update() call for perf. purposes. + /// + internal void InvalidateAdornerWindow(Region region) + { + if (DesignerFrameValid) + { + _designerFrame.Invalidate(region, true); + _designerFrame.Update(); + } + } + + /// + /// Invalidates the transparent AdornerWindow by asking the Designer Frame beneath it to invalidate. Note the they use of the .Update() call for perf. purposes. + /// + internal void InvalidateAdornerWindow(Rectangle rectangle) + { + if (DesignerFrameValid) + { + _designerFrame.Invalidate(rectangle, true); + _designerFrame.Update(); + } + } + + /// + /// The AdornerWindow intercepts all designer-related messages and forwards them to the BehaviorService for appropriate actions. Note that Paint and HitTest messages are correctly parsed and translated to AdornerWindow coords. + /// + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_NCHITTEST: + m.Result = (IntPtr)(NativeMethods.HTTRANSPARENT); + break; + default: + base.WndProc(ref m); + break; + } + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesigner.cs new file mode 100644 index 00000000000..ae0cba3275a --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesigner.cs @@ -0,0 +1,2635 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.ComponentModel.Design.Serialization; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Design; +using System.Globalization; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// Designer for the ToolStrip class. + /// + internal class ToolStripDesigner : ControlDesigner + { + private const int GLYPHBORDER = 2; + internal static Point s_lastCursorPosition = Point.Empty; //remembers last cursorPosition; + internal static bool s_autoAddNewItems = true; // true to force newly created items to be added to the currently selected strip. + internal static ToolStripItem s_dragItem = null; // this is used in overflow to know current item selected while drag, so that we can get the drop-index. + internal static bool s_shiftState = false; // maintains the shift state used of invalidation. disable csharp compiler warning #0414: field assigned unused value +#pragma warning disable 0414 + internal static bool s_editTemplateNode = false; // this is used in selection changed so that unnecessary redraw is not required. +#pragma warning restore 0414 + private DesignerToolStripControlHost _editorNode = null; //new editorNode + private ToolStripEditorManager _editManager = null; // newly added editor manager ... + private ToolStrip _miniToolStrip = null;// the toolStrip that hosts the "New Template Node" button + private DesignerTransaction _insertMenuItemTransaction = null; //There Should be one and only one Pending insertTransaction. + private Rectangle _dragBoxFromMouseDown = Rectangle.Empty; //Needed to Store the DRAGDROP Rect from the WinbarItemBehavior. + private int _indexOfItemUnderMouseToDrag = -1; //defaulted to invalid index andwill be set by the behaviour. + private ToolStripTemplateNode _tn = null; //templateNode + private ISelectionService _selectionSvc = null; // cached selection service. + private uint _editingCollection = 0; // non-zero if the collection editor is up for this winbar or a child of it. + private DesignerTransaction _pendingTransaction = null; // our transaction for adding/removing items. + private bool _addingItem = false; // true if we are expecting to be notified of adding a WinbarItem to the designer. + private Rectangle _boundsToInvalidate = Rectangle.Empty; //Bounds to Invalidate if a DropDownItem is Deleted + private bool _currentVisible = true; // Change Visibility + private ToolStripActionList _actionLists; // Action List on Chrome... + private ToolStripAdornerWindowService _toolStripAdornerWindowService = null; // Add the Adorner Service for OverFlow DropDown... + private IDesignerHost _host = null;//get private copy of the DesignerHost + private IComponentChangeService _componentChangeSvc; + private UndoEngine _undoEngine = null; + private bool _undoingCalled = false; + private IToolboxService _toolboxService; + private ContextMenuStrip _toolStripContextMenu; + private bool _toolStripSelected = false; + private bool _cacheItems = false; //ToolStripDesigner would cache items for the MenuItem when dropdown is changed. + private ArrayList _items; //cached Items. + private bool _disposed = false; + private DesignerTransaction _newItemTransaction; + private bool _fireSyncSelection = false; //fires SyncSelection when we toggle the items visibility to add the glyphs after the item gets visible. + private ToolStripKeyboardHandlingService _keyboardHandlingService = null; + private bool _parentNotVisible = false; //sync the parent visibility (used for ToolStripPanels) + private bool _dontCloseOverflow = false; //When an item is added to the ToolStrip through the templateNode which is on the Overflow; we should not close the overflow (to avoid flicker) + private bool _addingDummyItem = false; //When the dummyItem is added the toolStrip might resize (as in the Vertival Layouts). In this case we dont want the Resize to cause SyncSelection and Layouts. + + /// + /// Adds designer actions to the ActionLists collection. + /// + public override DesignerActionListCollection ActionLists + { + get + { + DesignerActionListCollection actionLists = new DesignerActionListCollection(); + actionLists.AddRange(base.ActionLists); + if (_actionLists == null) + { + _actionLists = new ToolStripActionList(this); + } + actionLists.Add(_actionLists); + + // First add the verbs for this component there... + DesignerVerbCollection verbs = Verbs; + if (verbs != null && verbs.Count != 0) + { + DesignerVerb[] verbsArray = new DesignerVerb[verbs.Count]; + verbs.CopyTo(verbsArray, 0); + actionLists.Add(new DesignerActionVerbList(verbsArray)); + } + return actionLists; + } + } + + /// + /// Compute the rect for the "Add New Item" button. + /// + private Rectangle AddItemRect + { + get + { + Rectangle rect = new Rectangle(); + if (_miniToolStrip == null) + { + return rect; + } + rect = _miniToolStrip.Bounds; + return rect; + } + } + + /// + /// Accessor for Shadow Property for AllowDrop. + /// + private bool AllowDrop + { + get => (bool)ShadowProperties["AllowDrop"]; + set + { + if (value && AllowItemReorder) + { + throw new ArgumentException(SR.ToolStripAllowItemReorderAndAllowDropCannotBeSetToTrue); + } + ShadowProperties["AllowDrop"] = value; + } + } + + /// + /// Accessor for Shadow Property for AllowItemReorder. + /// + private bool AllowItemReorder + { + get => (bool)ShadowProperties["AllowItemReorder"]; + set + { + if (value && AllowDrop) + { + throw new ArgumentException(SR.ToolStripAllowItemReorderAndAllowDropCannotBeSetToTrue); + } + ShadowProperties["AllowItemReorder"] = value; + } + } + + /// + /// The ToolStripItems are the associated components. We want those to come with in any cut, copy opreations. + /// + public override System.Collections.ICollection AssociatedComponents + { + get + { + ArrayList items = new ArrayList(); + foreach (ToolStripItem item in ToolStrip.Items) + { + if (!(item is DesignerToolStripControlHost addNewItem)) + { + items.Add(item); + } + } + return (ICollection)items; + } + } + + /// + /// CacheItems is set to TRUE by the ToolStripMenuItemDesigner, when the Transaction of setting the DropDown property is undone. In this case the Undo adds the original items to the Main MenustripDesigners Items collection and later are moved to to the appropriate ToolStripMenuItem + /// + public bool CacheItems + { + get => _cacheItems; + set => _cacheItems = value; + } + + /// + /// False if were inherited and can't be modified. + /// + private bool CanAddItems + { + get + { + // Make sure the component is not being inherited -- we can't delete these! + InheritanceAttribute ia = (InheritanceAttribute)TypeDescriptor.GetAttributes(ToolStrip)[typeof(InheritanceAttribute)]; + if (ia == null || ia.InheritanceLevel == InheritanceLevel.NotInherited) + { + return true; + } + return false; + } + } + + /// + // This boolean indicates whether the Control will allow SnapLines to be shown when any other targetControl is dragged on the design surface. This is true by default. + /// + internal override bool ControlSupportsSnaplines + { + get + { + if (!(ToolStrip.Parent is ToolStripPanel)) + { + return true; + } + return false; + } + } + + /// + /// DesignerContextMenu that is shown on the ToolStrip/MenuStrip/StatusStrip. + /// + private ContextMenuStrip DesignerContextMenu + { + get + { + if (_toolStripContextMenu == null) + { + _toolStripContextMenu = new BaseContextMenuStrip(ToolStrip.Site, ToolStrip) + { + Text = "CustomContextMenu" + }; + } + return _toolStripContextMenu; + } + } + + /// + /// Used by ToolStripTemplateNode. When the ToolStrip gains selection the Overflow is closed. But when an item is added through the TemplateNode which itself is on the Overflow, we should not close the Overflow as this caused weird artifacts and flicker. Hence this boolean property. + /// + public bool DontCloseOverflow + { + get => _dontCloseOverflow; + set => _dontCloseOverflow = value; + } + + /// + /// Since the Itemglyphs are recreated on the SelectionChanged, we need to cache in the "MouseDown" while the item Drag-Drop operation. + /// + public Rectangle DragBoxFromMouseDown + { + get => _dragBoxFromMouseDown; + set => _dragBoxFromMouseDown = value; + } + + /// + /// Set by the ToolStripItemCollectionEditor when it's launched for this winbar so we won't pick up it's items when added. We count this so that we can deal with nestings. + /// + internal bool EditingCollection + { + get => _editingCollection != 0; + set + { + if (value) + { + _editingCollection++; + } + else + { + _editingCollection--; + } + + } + } + + /// + /// EditManager for the ToolStrip Designer. This EditorManager controls the Insitu Editing. + /// + public ToolStripEditorManager EditManager + { + get => _editManager; + } + + /// + /// The TemplateNode. This is the object that actually creates miniToolStrip and manages InSitu editing. + /// + internal ToolStripTemplateNode Editor + { + get => _tn; + } + + /// + /// This is the ToolStripControlHost that hosts the ToolStripTemplateNode's miniToolStrip. + /// + public DesignerToolStripControlHost EditorNode + { + get => _editorNode; + } + + /// + /// This is the ToolStripTemplateNode's miniToolStrip. + /// + internal ToolStrip EditorToolStrip + { + get => _miniToolStrip; + set + { + _miniToolStrip = value; + _miniToolStrip.Parent = ToolStrip; + LayoutToolStrip(); + } + } + + /// + /// This will be set through ToolStripItemDesigner.SetItemVisible( ) if we find there is atleast one time that toggled from Visible==false to Visible==true In such a case we need to call BehaviorService.SyncSelection( ) toupdate the glyphs. + /// + public bool FireSyncSelection + { + get => _fireSyncSelection; + set => _fireSyncSelection = value; + } + + /// + /// Since the Itemglyphs are recreated on the SelectionChanged, we need to cache in the "index" of last MouseDown while the item Drag-Drop operation. + /// + public int IndexOfItemUnderMouseToDrag + { + get => _indexOfItemUnderMouseToDrag; + set => _indexOfItemUnderMouseToDrag = value; + } + + /// + /// ToolStrips if inherited act as ReadOnly. + /// + protected override InheritanceAttribute InheritanceAttribute + { + get + { + if ((base.InheritanceAttribute == InheritanceAttribute.Inherited)) + { + return InheritanceAttribute.InheritedReadOnly; + } + return base.InheritanceAttribute; + } + } + + /// + /// This is the insert Transaction. Now insert can happen at Main Menu level or the DropDown Level. This transaction is used to keep both in sync. + /// + public DesignerTransaction InsertTansaction + { + get => _insertMenuItemTransaction; + set => _insertMenuItemTransaction = value; + } + + /// + /// Checks if there is a seleciton of the ToolStrip or one of it's items. + /// + private bool IsToolStripOrItemSelected + { + get => _toolStripSelected; + } + + /// + /// CacheItems is set to TRUE by the ToolStripMenuItemDesigner, when the Transaction of setting the DropDown property is undone. In this case the Undo adds the original items to the Main MenustripDesigners Items collection and later are moved to to the appropriate ToolStripMenuItem. This is the Items Collection. + /// + public ArrayList Items + { + get + { + if (_items == null) + { + _items = new ArrayList(); + } + return _items; + } + } + + /// + /// This is the new item Transaction. This is used when the Insitu editor adds new Item. + /// + public DesignerTransaction NewItemTransaction + { + get => _newItemTransaction; + set => _newItemTransaction = value; + } + + /// + /// Compute the rect for the "OverFlow" button. + /// + private Rectangle OverFlowButtonRect + { + get + { + Rectangle rect = new Rectangle(); + if (ToolStrip.OverflowButton.Visible) + { + return ToolStrip.OverflowButton.Bounds; + } + else + { + return rect; + } + } + } + + /// + /// Get and cache the selection service + /// + internal ISelectionService SelectionService + { + get + { + if (_selectionSvc == null) + { + _selectionSvc = (ISelectionService)GetService(typeof(ISelectionService)); + Debug.Assert(_selectionSvc != null, "Failed to get Selection Service!"); + } + return _selectionSvc; + } + } + + public bool SupportEditing + { + get + { + if (GetService(typeof(DesignerOptionService)) is WindowsFormsDesignerOptionService dos) + { + return dos.CompatibilityOptions.EnableInSituEditing; + } + return true; + } + } + + /// + /// Handy way of gettting our ToolStrip + /// + protected ToolStrip ToolStrip + { + get => (ToolStrip)Component; + } + + /// + /// Get and cache the toolStripKeyBoard service + /// + private ToolStripKeyboardHandlingService KeyboardHandlingService + { + get + { + if (_keyboardHandlingService == null) + { + //Add the EditService so that the ToolStrip can do its own Tab and Keyboard Handling + _keyboardHandlingService = (ToolStripKeyboardHandlingService)GetService(typeof(ToolStripKeyboardHandlingService)); + if (_keyboardHandlingService == null) + { + _keyboardHandlingService = new ToolStripKeyboardHandlingService(Component.Site); + } + } + return _keyboardHandlingService; + } + } + + /// + /// There are certain containers (like ToolStrip) that require PerformLayout to be serialized in the code gen. + /// + internal override bool SerializePerformLayout + { + get => true; + } + + /// + /// Un - ShadowProperty. + /// + internal bool Visible + { + get => _currentVisible; + set + { + _currentVisible = value; + // If the user has set the Visible to false, sync the controls visible property. + if (ToolStrip.Visible != value && !SelectionService.GetComponentSelected(ToolStrip)) + { + Control.Visible = value; + } + } + } + + /// + /// This will add BodyGlyphs for the Items on the OverFlow. Since ToolStripItems are component we have to manage Adding and Deleting the glyphs ourSelves. + /// + private void AddBodyGlyphsForOverflow() + { + // now walk the winbar and add glyphs for each of it's children + foreach (ToolStripItem item in ToolStrip.Items) + { + if (item is DesignerToolStripControlHost) + { + continue; + } + // make sure it's on the Overflow... + if (item.Placement == ToolStripItemPlacement.Overflow) + { + AddItemBodyGlyph(item); + } + } + } + + /// + /// This will add BodyGlyphs for the Items on the OverFlow. Since ToolStripItems are component we have to manage Adding and Deleting the glyphs ourSelves. Called by AddBodyGlyphsForOverflow() + /// + private void AddItemBodyGlyph(ToolStripItem item) + { + if (item != null) + { + ToolStripItemDesigner dropDownItemDesigner = (ToolStripItemDesigner)_host.GetDesigner(item); + if (dropDownItemDesigner != null) + { + Rectangle bounds = dropDownItemDesigner.GetGlyphBounds(); + Behavior.Behavior toolStripBehavior = new ToolStripItemBehavior(); + // Initialize Glyph + ToolStripItemGlyph bodyGlyphForddItem = new ToolStripItemGlyph(item, dropDownItemDesigner, bounds, toolStripBehavior); + //Set the glyph for the item .. so that we can remove it later.... + dropDownItemDesigner.bodyGlyph = bodyGlyphForddItem; + //Add ItemGlyph to the Collection + if (_toolStripAdornerWindowService != null) + { + _toolStripAdornerWindowService.DropDownAdorner.Glyphs.Add(bodyGlyphForddItem); + } + } + } + } + + /// + /// Fired when a new item is chosen from the AddItems menu from the Template Node. + /// + private ToolStripItem AddNewItem(Type t) + { + Debug.Assert(_host != null, "Why didn't we get a designer host?"); + NewItemTransaction = _host.CreateTransaction(SR.ToolStripCreatingNewItemTransaction); + IComponent component = null; + try + { + _addingItem = true; + // Suspend the Layout as we are about to add Item to the ToolStrip + ToolStrip.SuspendLayout(); + ToolStripItemDesigner designer = null; + try + { + // The code in ComponentAdded will actually get the add done. This should be inside the try finally because it could throw an exception and keep the toolstrip in SuspendLayout mode + component = _host.CreateComponent(t); + designer = _host.GetDesigner(component) as ToolStripItemDesigner; + designer.InternalCreate = true; + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + } + finally + { + if (designer != null) + { + designer.InternalCreate = false; + } + // Resume the Layout as we are about to add Item to the ToolStrip + ToolStrip.ResumeLayout(); + } + } + catch (Exception e) + { + if (NewItemTransaction != null) + { + NewItemTransaction.Cancel(); + NewItemTransaction = null; + } + + // Throw the exception unless it's a canceled checkout + if ((!(e is CheckoutException checkoutException)) || (!checkoutException.Equals(CheckoutException.Canceled))) + { + throw; + } + } + finally + { + _addingItem = false; + } + return component as ToolStripItem; + } + + // Standard 'catch all - rethrow critical' exception pattern + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + internal ToolStripItem AddNewItem(Type t, string text, bool enterKeyPressed, bool tabKeyPressed) + { + Debug.Assert(_host != null, "Why didn't we get a designer host?"); + Debug.Assert(_pendingTransaction == null, "Adding item with pending transaction?"); + DesignerTransaction outerTransaction = _host.CreateTransaction(string.Format(SR.ToolStripAddingItem, t.Name)); + ToolStripItem item = null; + try + { + _addingItem = true; + // Suspend the Layout as we are about to add Item to the ToolStrip + ToolStrip.SuspendLayout(); + // The code in ComponentAdded will actually get the add done. + IComponent component = _host.CreateComponent(t, NameFromText(text, t, Component.Site)); + ToolStripItemDesigner designer = _host.GetDesigner(component) as ToolStripItemDesigner; + try + { + // ToolStripItem designer tries to set the TEXT for the item in the InitializeNewComponent(). But since we are create item thru InSitu .. we shouldnt do this. Also we shouldn't set the TEXT if we are creating a dummyItem. + if (!string.IsNullOrEmpty(text)) + { + designer.InternalCreate = true; + } + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + } + finally + { + designer.InternalCreate = false; + } + + //Set the Text and Image.. + item = component as ToolStripItem; + if (item != null) + { + PropertyDescriptor textProperty = TypeDescriptor.GetProperties(item)["Text"]; + Debug.Assert(textProperty != null, "Could not find 'Text' property in ToolStripItem."); + if (textProperty != null && !string.IsNullOrEmpty(text)) + { + textProperty.SetValue(item, text); + } + + //Set the Image property and DisplayStyle... + if (item is ToolStripButton || item is ToolStripSplitButton || item is ToolStripDropDownButton) + { + Image image = null; + try + { + image = new Bitmap(typeof(ToolStripButton), "blank.bmp"); + } + catch (Exception e) + { + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + } + + PropertyDescriptor imageProperty = TypeDescriptor.GetProperties(item)["Image"]; + Debug.Assert(imageProperty != null, "Could not find 'Image' property in ToolStripItem."); + if (imageProperty != null && image != null) + { + imageProperty.SetValue(item, image); + } + + PropertyDescriptor dispProperty = TypeDescriptor.GetProperties(item)["DisplayStyle"]; + Debug.Assert(dispProperty != null, "Could not find 'DisplayStyle' property in ToolStripItem."); + if (dispProperty != null) + { + dispProperty.SetValue(item, ToolStripItemDisplayStyle.Image); + } + + PropertyDescriptor imageTransProperty = TypeDescriptor.GetProperties(item)["ImageTransparentColor"]; + Debug.Assert(imageTransProperty != null, "Could not find 'DisplayStyle' property in ToolStripItem."); + if (imageTransProperty != null) + { + imageTransProperty.SetValue(item, Color.Magenta); + } + } + } + + // ResumeLayout on ToolStrip. + ToolStrip.ResumeLayout(); + if (!tabKeyPressed) + { + if (enterKeyPressed) + { + if (!designer.SetSelection(enterKeyPressed)) + { + if (KeyboardHandlingService != null) + { + KeyboardHandlingService.SelectedDesignerControl = _editorNode; + SelectionService.SetSelectedComponents(null, SelectionTypes.Replace); + } + } + } + else + { + // put the templateNode into nonselection mode && select the existing Item + KeyboardHandlingService.SelectedDesignerControl = null; + SelectionService.SetSelectedComponents(new IComponent[] { item }, SelectionTypes.Replace); + _editorNode.RefreshSelectionGlyph(); + } + } + else + { + if (_keyboardHandlingService != null) + { + KeyboardHandlingService.SelectedDesignerControl = _editorNode; + SelectionService.SetSelectedComponents(null, SelectionTypes.Replace); + } + } + + if (designer != null && item.Placement != ToolStripItemPlacement.Overflow) + { + Rectangle bounds = designer.GetGlyphBounds(); + SelectionManager selMgr = (SelectionManager)GetService(typeof(SelectionManager)); + Behavior.Behavior toolStripBehavior = new ToolStripItemBehavior(); + ToolStripItemGlyph bodyGlyphForItem = new ToolStripItemGlyph(item, designer, bounds, toolStripBehavior); + //Add ItemGlyph to the Collection + selMgr.BodyGlyphAdorner.Glyphs.Insert(0, bodyGlyphForItem); + } + else if (designer != null && item.Placement == ToolStripItemPlacement.Overflow) + { + // Add Glyphs for overflow... + RemoveBodyGlyphsForOverflow(); + AddBodyGlyphsForOverflow(); + } + } + catch (Exception exception) + { + // ResumeLayout on ToolStrip. + ToolStrip.ResumeLayout(); + if (_pendingTransaction != null) + { + _pendingTransaction.Cancel(); + _pendingTransaction = null; + } + if (outerTransaction != null) + { + outerTransaction.Cancel(); + outerTransaction = null; + } + + if (exception is CheckoutException checkoutEx && checkoutEx != CheckoutException.Canceled) + { + throw; + } + } + finally + { + if (_pendingTransaction != null) + { + _pendingTransaction.Cancel(); + _pendingTransaction = null; + + if (outerTransaction != null) + { + outerTransaction.Cancel(); + } + } + else if (outerTransaction != null) + { + outerTransaction.Commit(); + outerTransaction = null; + } + _addingItem = false; + } + return item; + } + + // + // Adds the new TemplateNode to the ToolStrip or MenuStrip. + // + internal void AddNewTemplateNode(ToolStrip wb) + { + // setup the MINIToolStrip host... + _tn = new ToolStripTemplateNode(Component, SR.ToolStripDesignerTemplateNodeEnterText, null); + _miniToolStrip = _tn.EditorToolStrip; + int width = _tn.EditorToolStrip.Width; + _editorNode = new DesignerToolStripControlHost(_tn.EditorToolStrip); + _tn.ControlHost = _editorNode; + _editorNode.Width = width; + ToolStrip.Items.Add(_editorNode); + _editorNode.Visible = false; + } + + internal void CancelPendingMenuItemTransaction() + { + if (_insertMenuItemTransaction != null) + { + _insertMenuItemTransaction.Cancel(); + } + } + + // + // Check if the ToolStripItems are selected. + // + private bool CheckIfItemSelected() + { + bool showToolStrip = false; + object comp = SelectionService.PrimarySelection; + if (comp == null) + { + comp = (IComponent)KeyboardHandlingService.SelectedDesignerControl; + } + if (comp is ToolStripItem item) + { + if (item.Placement == ToolStripItemPlacement.Overflow && item.Owner == ToolStrip) + { + if (ToolStrip.CanOverflow && !ToolStrip.OverflowButton.DropDown.Visible) + { + ToolStrip.OverflowButton.ShowDropDown(); + } + showToolStrip = true; + } + else + { + + if (!ItemParentIsOverflow(item)) + { + if (ToolStrip.OverflowButton.DropDown.Visible) + { + ToolStrip.OverflowButton.HideDropDown(); + } + } + if (item.Owner == ToolStrip) + { + showToolStrip = true; + } + else if (item is DesignerToolStripControlHost) + { + if (item.IsOnDropDown && item.Placement != ToolStripItemPlacement.Overflow) + { + ToolStripDropDown dropDown = (ToolStripDropDown)((DesignerToolStripControlHost)comp).GetCurrentParent(); + if (dropDown != null) + { + ToolStripItem ownerItem = dropDown.OwnerItem; + ToolStripMenuItemDesigner itemDesigner = (ToolStripMenuItemDesigner)_host.GetDesigner(ownerItem); + ToolStripDropDown topmost = itemDesigner.GetFirstDropDown((ToolStripDropDownItem)(ownerItem)); + ToolStripItem topMostItem = (topmost == null) ? ownerItem : topmost.OwnerItem; + + if (topMostItem != null && topMostItem.Owner == ToolStrip) + { + showToolStrip = true; + } + } + } + } + else if (item.IsOnDropDown && item.Placement != ToolStripItemPlacement.Overflow) + { + ToolStripItem parentItem = ((ToolStripDropDown)(item.Owner)).OwnerItem; + if (parentItem != null) + { + ToolStripMenuItemDesigner itemDesigner = (ToolStripMenuItemDesigner)_host.GetDesigner(parentItem); + ToolStripDropDown topmost = (itemDesigner == null) ? null : itemDesigner.GetFirstDropDown((ToolStripDropDownItem)parentItem); + ToolStripItem topMostItem = (topmost == null) ? parentItem : topmost.OwnerItem; + if (topMostItem != null && topMostItem.Owner == ToolStrip) + { + showToolStrip = true; + } + } + } + } + } + return showToolStrip; + } + + // + // This is called ToolStripItemGlyph to commit the TemplateNode Edition on the Parent ToolStrip. + // + internal bool Commit() + { + if (_tn != null && _tn.Active) + { + _tn.Commit(false, false); + _editorNode.Width = _tn.EditorToolStrip.Width; + } + else + { + if (SelectionService.PrimarySelection is ToolStripDropDownItem selectedItem) + { + if (_host.GetDesigner(selectedItem) is ToolStripMenuItemDesigner itemDesigner && itemDesigner.IsEditorActive) + { + itemDesigner.Commit(); + return true; + } + } + else + { + if (KeyboardHandlingService != null) + { + if (KeyboardHandlingService.SelectedDesignerControl is ToolStripItem designerItem && designerItem.IsOnDropDown) + { + if (designerItem.GetCurrentParent() is ToolStripDropDown parent) + { + if (parent.OwnerItem is ToolStripDropDownItem ownerItem) + { + if (_host.GetDesigner(ownerItem) is ToolStripMenuItemDesigner itemDesigner && itemDesigner.IsEditorActive) + { + itemDesigner.Commit(); + return true; + } + } + } + } + else + { //check for normal ToolStripItem selection .... + if (SelectionService.PrimarySelection is ToolStripItem toolItem) + { + ToolStripItemDesigner itemDesigner = (ToolStripItemDesigner)_host.GetDesigner(toolItem); + if (itemDesigner != null && itemDesigner.IsEditorActive) + { + itemDesigner.Editor.Commit(false, false); + return true; + } + } + } + } + } + } + return false; + } + + /// + /// Make sure the AddNewItem button is setup properly. + /// + private void Control_HandleCreated(object sender, EventArgs e) + { + Control.HandleCreated -= new EventHandler(Control_HandleCreated); + InitializeNewItemDropDown(); + // we should HOOK this event here since getting the OverFlowButton property causes handle creation which we need to avoid hook the designer for the OverFlow Item events.. + ToolStrip.OverflowButton.DropDown.Closing += new ToolStripDropDownClosingEventHandler(OnOverflowDropDownClosing); + ToolStrip.OverflowButton.DropDownOpening += new EventHandler(OnOverFlowDropDownOpening); + ToolStrip.OverflowButton.DropDownOpened += new EventHandler(OnOverFlowDropDownOpened); + ToolStrip.OverflowButton.DropDownClosed += new EventHandler(OnOverFlowDropDownClosed); + ToolStrip.OverflowButton.DropDown.Resize += new System.EventHandler(OnOverflowDropDownResize); + ToolStrip.OverflowButton.DropDown.Paint += new System.Windows.Forms.PaintEventHandler(OnOverFlowDropDownPaint); + ToolStrip.Move += new System.EventHandler(OnToolStripMove); + ToolStrip.VisibleChanged += new System.EventHandler(OnToolStripVisibleChanged); + ToolStrip.ItemAdded += new ToolStripItemEventHandler(OnItemAdded); + } + + /// + /// Fired after a component has been added. Here, we add it to the winbar and select it. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private void ComponentChangeSvc_ComponentAdded(object sender, ComponentEventArgs e) + { + // If another ToolStrip is getting added and we are currently selected then unselect us .. the newly added toolStrip should get selected. + if (_toolStripSelected && e.Component is ToolStrip) + { + _toolStripSelected = false; + } + try + { + // make sure it's one of ours and not on DropDown. + if (e.Component is ToolStripItem newItem && _addingItem && !newItem.IsOnDropDown) + { + _addingItem = false; + if (CacheItems) + { + _items.Add(newItem); + } + else + { + // Get the current count of ToolStripItems. + int count = ToolStrip.Items.Count; + // notify the designer what's changed. + try + { + base.RaiseComponentChanging(TypeDescriptor.GetProperties(Component)["Items"]); + if (SelectionService.PrimarySelection is ToolStripItem selectedItem) + { + //ADD at the current Selection ... + if (selectedItem.Owner == ToolStrip) + { + int indexToInsert = ToolStrip.Items.IndexOf(selectedItem); + ToolStrip.Items.Insert(indexToInsert, newItem); + } + + } + else if (count > 0) + { + // ADD at Last but one, the last one being the TemplateNode always... + ToolStrip.Items.Insert(count - 1, newItem); + } + else + { + ToolStrip.Items.Add(newItem); + } + } + finally + { + base.RaiseComponentChanged(TypeDescriptor.GetProperties(Component)["Items"], null, null); + } + } + } + } + catch + { + if (_pendingTransaction != null) + { + _pendingTransaction.Cancel(); + _pendingTransaction = null; + _insertMenuItemTransaction = null; + } + } + finally + { + if (_pendingTransaction != null) + { + _pendingTransaction.Commit(); + _pendingTransaction = null; + _insertMenuItemTransaction = null; + } + } + } + + /// + /// Checks if the component being added is a child ToolStripItem. + /// + private void ComponentChangeSvc_ComponentAdding(object sender, ComponentEventArgs e) + { + if (KeyboardHandlingService != null && KeyboardHandlingService.CopyInProgress) + { + return; + } + + // Return if we are not the owner !! + object selectedItem = SelectionService.PrimarySelection; + if (selectedItem == null) + { + if (_keyboardHandlingService != null) + { + selectedItem = KeyboardHandlingService.SelectedDesignerControl; + } + } + if (selectedItem is ToolStripItem currentSel && currentSel.Owner != ToolStrip) + { + return; + } + + // we'll be adding a child item if the component is a winbar item and we've currently got this winbar or one of it's items selected. we do this so things like paste and undo automagically work. + ToolStripItem addingItem = e.Component as ToolStripItem; + if (addingItem != null && addingItem.Owner != null) + { + if (addingItem.Owner.Site == null) + { + //we are DummyItem to the ToolStrip... + return; + } + } + if (_insertMenuItemTransaction == null && s_autoAddNewItems && addingItem != null && !_addingItem && IsToolStripOrItemSelected && !EditingCollection) + { + _addingItem = true; + + if (_pendingTransaction == null) + { + Debug.Assert(_host != null, "Why didn't we get a designer host?"); + _insertMenuItemTransaction = _pendingTransaction = _host.CreateTransaction(SR.ToolStripDesignerTransactionAddingItem); + } + } + } + + /// + /// Required to check if we need to show the Overflow, if any change has caused the item to go into the overflow. + /// + private void ComponentChangeSvc_ComponentChanged(object sender, ComponentChangedEventArgs e) + { + if (e.Component is ToolStripItem changingItem) + { + ToolStrip parent = changingItem.Owner; + if (parent == ToolStrip && e.Member != null && e.Member.Name == "Overflow") + { + ToolStripItemOverflow oldValue = (ToolStripItemOverflow)e.OldValue; + ToolStripItemOverflow newValue = (ToolStripItemOverflow)e.NewValue; + if (oldValue != ToolStripItemOverflow.Always && newValue == ToolStripItemOverflow.Always) + { + // If now the Item falls in the Overflow .. Open the Overflow.. + if (ToolStrip.CanOverflow && !ToolStrip.OverflowButton.DropDown.Visible) + { + ToolStrip.OverflowButton.ShowDropDown(); + } + } + } + } + } + + /// + /// After a ToolStripItem is removed, remove it from the ToolStrip and select the next item. + /// + private void ComponentChangeSvc_ComponentRemoved(object sender, ComponentEventArgs e) + { + if (e.Component is ToolStripItem && ((ToolStripItem)e.Component).Owner == Component) + { + ToolStripItem item = (ToolStripItem)e.Component; + int itemIndex = ToolStrip.Items.IndexOf(item); + // send notifications. + try + { + if (itemIndex != -1) + { + ToolStrip.Items.Remove(item); + base.RaiseComponentChanged(TypeDescriptor.GetProperties(Component)["Items"], null, null); + } + } + finally + { + if (_pendingTransaction != null) + { + _pendingTransaction.Commit(); + _pendingTransaction = null; + } + } + + // select the next item or the ToolStrip itself. + if (ToolStrip.Items.Count > 1) + { + itemIndex = Math.Min(ToolStrip.Items.Count - 1, itemIndex); + itemIndex = Math.Max(0, itemIndex); + } + else + { + itemIndex = -1; + } + LayoutToolStrip(); + + //Reset the Glyphs if the item removed is on the OVERFLOW, + if (item.Placement == ToolStripItemPlacement.Overflow) + { + // Add Glyphs for overflow... + RemoveBodyGlyphsForOverflow(); + AddBodyGlyphsForOverflow(); + } + + if (_toolStripAdornerWindowService != null && _boundsToInvalidate != Rectangle.Empty) + { + _toolStripAdornerWindowService.Invalidate(_boundsToInvalidate); + BehaviorService.Invalidate(_boundsToInvalidate); + } + + if (KeyboardHandlingService.CutOrDeleteInProgress) + { + IComponent targetSelection = (itemIndex == -1) ? (IComponent)ToolStrip : (IComponent)ToolStrip.Items[itemIndex]; + // if the TemplateNode becomes the targetSelection, then set the targetSelection to null. + if (targetSelection != null) + { + if (targetSelection is DesignerToolStripControlHost) + { + if (KeyboardHandlingService != null) + { + KeyboardHandlingService.SelectedDesignerControl = targetSelection; + } + SelectionService.SetSelectedComponents(null, SelectionTypes.Replace); + + } + else + { + SelectionService.SetSelectedComponents(new IComponent[] { targetSelection }, SelectionTypes.Replace); + } + } + } + } + } + + /// + /// Before a ToolStripItem is removed, open a transaction to batch the operation. + /// + private void ComponentChangeSvc_ComponentRemoving(object sender, ComponentEventArgs e) + { + if (e.Component is ToolStripItem && ((ToolStripItem)e.Component).Owner == Component) + { + Debug.Assert(_host != null, "Why didn't we get a designer host?"); + Debug.Assert(_pendingTransaction == null, "Removing item with pending transaction?"); + try + { + _pendingTransaction = _host.CreateTransaction(SR.ToolStripDesignerTransactionRemovingItem); + base.RaiseComponentChanging(TypeDescriptor.GetProperties(Component)["Items"]); + if (e.Component is ToolStripDropDownItem dropDownItem) + { + dropDownItem.HideDropDown(); + _boundsToInvalidate = dropDownItem.DropDown.Bounds; + } + } + catch + { + if (_pendingTransaction != null) + { + _pendingTransaction.Cancel(); + _pendingTransaction = null; + } + } + } + } + + /// + /// Clean up the mess we've made! + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + _disposed = true; + if (_items != null) + { + _items = null; + } + if (_undoEngine != null) + { + _undoEngine.Undoing -= new EventHandler(OnUndoing); + _undoEngine.Undone -= new EventHandler(OnUndone); + } + // unhook notifications. + if (_componentChangeSvc != null) + { + _componentChangeSvc.ComponentRemoved -= new ComponentEventHandler(ComponentChangeSvc_ComponentRemoved); + _componentChangeSvc.ComponentRemoving -= new ComponentEventHandler(ComponentChangeSvc_ComponentRemoving); + _componentChangeSvc.ComponentAdded -= new ComponentEventHandler(ComponentChangeSvc_ComponentAdded); + _componentChangeSvc.ComponentAdding -= new ComponentEventHandler(ComponentChangeSvc_ComponentAdding); + _componentChangeSvc.ComponentChanged -= new ComponentChangedEventHandler(ComponentChangeSvc_ComponentChanged); + } + if (_selectionSvc != null) + { + _selectionSvc.SelectionChanged -= new EventHandler(SelSvc_SelectionChanged); + _selectionSvc.SelectionChanging -= new EventHandler(SelSvc_SelectionChanging); + _selectionSvc = null; + } + + EnableDragDrop(false); + //Dispose of the EitManager + if (_editManager != null) + { + _editManager.CloseManager(); + _editManager = null; + } + + //tear down the TemplateNode + if (_tn != null) + { + _tn.RollBack(); + _tn.CloseEditor(); + _tn = null; + } + + // teardown the add item button. + if (_miniToolStrip != null) + { + _miniToolStrip.Dispose(); + _miniToolStrip = null; + } + + //tearDown the EditorNode.. + if (_editorNode != null) + { + _editorNode.Dispose(); + _editorNode = null; + } + + if (ToolStrip != null) + { + ToolStrip.OverflowButton.DropDown.Closing -= new ToolStripDropDownClosingEventHandler(OnOverflowDropDownClosing); + ToolStrip.OverflowButton.DropDownOpening -= new EventHandler(OnOverFlowDropDownOpening); + ToolStrip.OverflowButton.DropDownOpened -= new EventHandler(OnOverFlowDropDownOpened); + ToolStrip.OverflowButton.DropDownClosed -= new EventHandler(OnOverFlowDropDownClosed); + ToolStrip.OverflowButton.DropDown.Resize -= new EventHandler(OnOverflowDropDownResize); + ToolStrip.OverflowButton.DropDown.Paint -= new PaintEventHandler(OnOverFlowDropDownPaint); + + ToolStrip.Move -= new System.EventHandler(OnToolStripMove); + ToolStrip.VisibleChanged -= new System.EventHandler(OnToolStripVisibleChanged); + ToolStrip.ItemAdded -= new ToolStripItemEventHandler(OnItemAdded); + ToolStrip.Resize -= new EventHandler(ToolStrip_Resize); + ToolStrip.DockChanged -= new EventHandler(ToolStrip_Resize); + ToolStrip.LayoutCompleted -= new EventHandler(ToolStrip_LayoutCompleted); + } + // tear off the ContextMenu.. + if (_toolStripContextMenu != null) + { + _toolStripContextMenu.Dispose(); + _toolStripContextMenu = null; + } + //Always Remove all the glyphs we added + RemoveBodyGlyphsForOverflow(); + //tear off the OverFlow if its being shown + if (ToolStrip.OverflowButton.DropDown.Visible) + { + ToolStrip.OverflowButton.HideDropDown(); + } + + if (_toolStripAdornerWindowService != null) + { + _toolStripAdornerWindowService = null; + } + } + base.Dispose(disposing); + } + + /// + /// Creates a method signature in the source code file for the default event on the component and navigates the user's cursor to that location in preparation to assign the default action. + /// + public override void DoDefaultAction() + { + //Dont Fire the Events if the Component is Inherited. + if (InheritanceAttribute != InheritanceAttribute.InheritedReadOnly) + { + IComponent selectedItem = SelectionService.PrimarySelection as IComponent; + if (selectedItem == null) + { + if (KeyboardHandlingService != null) + { + selectedItem = (IComponent)KeyboardHandlingService.SelectedDesignerControl; + } + + } + // if one of the sub-items is selected, delegate to it. + if (selectedItem is ToolStripItem) + { + if (_host != null) + { + IDesigner itemDesigner = _host.GetDesigner(selectedItem); + if (itemDesigner != null) + { + itemDesigner.DoDefaultAction(); + return; + } + } + } + base.DoDefaultAction(); + } + } + + + /// + /// We add our BodyGlyphs as well as bodyGlyphs for the ToolStripItems here. + /// + protected override ControlBodyGlyph GetControlGlyph(GlyphSelectionType selectionType) + { + // Get the glyphs iff Handle is created for the toolStrip. + if (!ToolStrip.IsHandleCreated) + { + return null; + } + SelectionManager selMgr = (SelectionManager)GetService(typeof(SelectionManager)); + if (selMgr != null && ToolStrip != null && CanAddItems && ToolStrip.Visible) + { + object primarySelection = SelectionService.PrimarySelection; + Behavior.Behavior toolStripBehavior = new ToolStripItemBehavior(); + //sometimes the Collection changes when the ToolStrip gets the Selection and we are in Dummy Insitu edit... so remove that before you access the collection.. + if (ToolStrip.Items.Count > 0) + { + ToolStripItem[] items = new ToolStripItem[ToolStrip.Items.Count]; + ToolStrip.Items.CopyTo(items, 0); + foreach (ToolStripItem toolItem in items) + { + if (toolItem != null) + { + ToolStripItemDesigner itemDesigner = (ToolStripItemDesigner)_host.GetDesigner(toolItem); + bool isPrimary = (toolItem == primarySelection); + if (!isPrimary && + itemDesigner != null && + itemDesigner.IsEditorActive) + { + + itemDesigner.Editor.Commit(false, false); + } + } + } + } + + // Check if menuEditor is present and active... + IMenuEditorService menuEditorService = (IMenuEditorService)GetService(typeof(IMenuEditorService)); + if (menuEditorService == null || (menuEditorService != null && !menuEditorService.IsActive())) + { + + // now walk the winbar and add glyphs for each of it's children + foreach (ToolStripItem item in ToolStrip.Items) + { + if (item is DesignerToolStripControlHost) + { + continue; + } + // make sure it's on the winbar... + if (item.Placement == ToolStripItemPlacement.Main) + { + ToolStripItemDesigner itemDesigner = (ToolStripItemDesigner)_host.GetDesigner(item); + if (itemDesigner != null) + { + bool isPrimary = (item == primarySelection); + if (isPrimary) + { + ((ToolStripItemBehavior)toolStripBehavior)._dragBoxFromMouseDown = _dragBoxFromMouseDown; + } + + // Get Back the Current Bounds if current selection is not a primary selection + if (!isPrimary) + { + item.AutoSize = (itemDesigner != null) ? itemDesigner.AutoSize : true; + } + + Rectangle itemBounds = itemDesigner.GetGlyphBounds(); + Control parent = ToolStrip.Parent; + Rectangle parentBounds = BehaviorService.ControlRectInAdornerWindow(parent); + if (IsGlyphTotallyVisible(itemBounds, parentBounds) && item.Visible) + { + // Add Glyph ONLY AFTER item width is changed... + ToolStripItemGlyph bodyGlyphForItem = new ToolStripItemGlyph(item, itemDesigner, itemBounds, toolStripBehavior); + itemDesigner.bodyGlyph = bodyGlyphForItem; + //Add ItemGlyph to the Collection + selMgr.BodyGlyphAdorner.Glyphs.Add(bodyGlyphForItem); + } + } + } + } + } + } + return (base.GetControlGlyph(selectionType)); + } + + /// + /// We add our SelectionGlyphs here. Since ToolStripItems are components we add the SelectionGlyphs for those in this call as well. + /// + public override GlyphCollection GetGlyphs(GlyphSelectionType selType) + { + // get the default glyphs for this component. + GlyphCollection glyphs = new GlyphCollection(); + ICollection selComponents = SelectionService.GetSelectedComponents(); + foreach (object comp in selComponents) + { + if (comp is ToolStrip) + { + GlyphCollection toolStripGlyphs = base.GetGlyphs(selType); + glyphs.AddRange(toolStripGlyphs); + } + else + { + if (comp is ToolStripItem item && item.Visible) + { + ToolStripItemDesigner itemDesigner = (ToolStripItemDesigner)_host.GetDesigner(item); + if (itemDesigner != null) + { + itemDesigner.GetGlyphs(ref glyphs, StandardBehavior); + } + } + } + } + + if ((SelectionRules & SelectionRules.Moveable) != 0 && InheritanceAttribute != InheritanceAttribute.InheritedReadOnly && (selType != GlyphSelectionType.NotSelected)) + { + //get the adornerwindow-relative coords for the container control + Point loc = BehaviorService.ControlToAdornerWindow((Control)Component); + Rectangle translatedBounds = new Rectangle(loc, ((Control)Component).Size); + int glyphOffset = (int)(DesignerUtils.CONTAINERGRABHANDLESIZE * .5); + //if the control is too small for our ideal position... + if (translatedBounds.Width < 2 * DesignerUtils.CONTAINERGRABHANDLESIZE) + { + glyphOffset = -1 * glyphOffset; + } + + ContainerSelectorBehavior behavior = new ContainerSelectorBehavior(ToolStrip, Component.Site, true); + ContainerSelectorGlyph containerSelectorGlyph = new ContainerSelectorGlyph(translatedBounds, DesignerUtils.CONTAINERGRABHANDLESIZE, glyphOffset, behavior); + glyphs.Insert(0, containerSelectorGlyph); + } + return glyphs; + } + + /// + /// Allow hit testing over the AddNewItem button only. + /// + protected override bool GetHitTest(Point point) + { + // convert to client coords. + point = Control.PointToClient(point); + + if (_miniToolStrip != null && _miniToolStrip.Visible && AddItemRect.Contains(point)) + { + return true; + } + if (OverFlowButtonRect.Contains(point)) + { + return true; + } + return base.GetHitTest(point); + + } + + /// + /// Get the designer set up to run. + /// + // EditorServiceContext is newed up to add Edit Items verb. + [SuppressMessage("Microsoft.Performance", "CA1806:DoNotIgnoreMethodResults")] + public override void Initialize(IComponent component) + { + base.Initialize(component); + AutoResizeHandles = true; + _host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (_host != null) + { + _componentChangeSvc = (IComponentChangeService)_host.GetService(typeof(IComponentChangeService)); + } + + if (_undoEngine == null) + { + _undoEngine = GetService(typeof(UndoEngine)) as UndoEngine; + if (_undoEngine != null) + { + _undoEngine.Undoing += new EventHandler(OnUndoing); + _undoEngine.Undone += new EventHandler(OnUndone); + } + } + + // initialize new Manager For Editing winbars + _editManager = new ToolStripEditorManager(component); + // setup the dropdown if our handle has been created. + if (Control.IsHandleCreated) + { + InitializeNewItemDropDown(); + } + else + { + Control.HandleCreated += new EventHandler(Control_HandleCreated); + } + // attach notifcations. + if (_componentChangeSvc != null) + { + _componentChangeSvc.ComponentRemoved += new ComponentEventHandler(ComponentChangeSvc_ComponentRemoved); + _componentChangeSvc.ComponentRemoving += new ComponentEventHandler(ComponentChangeSvc_ComponentRemoving); + _componentChangeSvc.ComponentAdded += new ComponentEventHandler(ComponentChangeSvc_ComponentAdded); + _componentChangeSvc.ComponentAdding += new ComponentEventHandler(ComponentChangeSvc_ComponentAdding); + _componentChangeSvc.ComponentChanged += new ComponentChangedEventHandler(ComponentChangeSvc_ComponentChanged); + } + + //hookup to the AdornerService..for the overflow dropdown to be parent properly. + _toolStripAdornerWindowService = (ToolStripAdornerWindowService)GetService(typeof(ToolStripAdornerWindowService)); + SelectionService.SelectionChanging += new EventHandler(SelSvc_SelectionChanging); + SelectionService.SelectionChanged += new EventHandler(SelSvc_SelectionChanged); + + // sink size changes. + ToolStrip.Resize += new EventHandler(ToolStrip_Resize); + ToolStrip.DockChanged += new EventHandler(ToolStrip_Resize); + ToolStrip.LayoutCompleted += new EventHandler(ToolStrip_LayoutCompleted); + + // Make sure the overflow is not toplevel + ToolStrip.OverflowButton.DropDown.TopLevel = false; + + // init the verb. + if (CanAddItems) + { + new EditorServiceContext(this, TypeDescriptor.GetProperties(Component)["Items"], SR.ToolStripItemCollectionEditorVerb); + //Add the EditService so that the ToolStrip can do its own Tab and Keyboard Handling + _keyboardHandlingService = (ToolStripKeyboardHandlingService)GetService(typeof(ToolStripKeyboardHandlingService)); + if (_keyboardHandlingService == null) + { + _keyboardHandlingService = new ToolStripKeyboardHandlingService(Component.Site); + } + + //Add the InsituEditService so that the ToolStrip can do its own Tab and Keyboard Handling + ISupportInSituService inSituService = (ISupportInSituService)GetService(typeof(ISupportInSituService)); + if (inSituService == null) + { + inSituService = new ToolStripInSituService(Component.Site); + } + } + + // ToolStrip is selected... + _toolStripSelected = true; + // Reset the TemplateNode Selection if any... + if (_keyboardHandlingService != null) + { + KeyboardHandlingService.SelectedDesignerControl = null; + } + } + + /// + /// ControlDesigner overrides this method. It will look at the default property for the control and, if it is of type string, it will set this property's value to the name of the component. It only does this if the designer has been configured with this option in the options service. This method also connects the control to its parent and positions it. If you override this method, you should always call base. + /// + public override void InitializeNewComponent(IDictionary defaultValues) + { + Control parent = defaultValues["Parent"] as Control; + Form parentForm = _host.RootComponent as Form; + MainMenu parentMenu = null; + FormDocumentDesigner parentFormDesigner = null; + if (parentForm != null) + { + parentFormDesigner = _host.GetDesigner(parentForm) as FormDocumentDesigner; + if (parentFormDesigner != null && parentFormDesigner.Menu != null) + { + // stash off the main menu while we initialize + parentMenu = parentFormDesigner.Menu; + parentFormDesigner.Menu = null; + } + } + + ToolStripPanel parentPanel = parent as ToolStripPanel; + // smoke the Dock Property if the toolStrip is getting parented to the ContentPanel. + if (parentPanel == null && parent is ToolStripContentPanel) + { + // smoke the dock property whenever we add a toolstrip to a toolstrip panel. + PropertyDescriptor dockProp = TypeDescriptor.GetProperties(ToolStrip)["Dock"]; + if (dockProp != null) + { + dockProp.SetValue(ToolStrip, DockStyle.None); + } + } + + /// set up parenting and all the base stuff... + if (parentPanel == null || ToolStrip is MenuStrip) + { + base.InitializeNewComponent(defaultValues); + } + + if (parentFormDesigner != null) + { + //Add MenuBack + if (parentMenu != null) + { + parentFormDesigner.Menu = parentMenu; + } + //Set MainMenuStrip property + if (ToolStrip is MenuStrip) + { + PropertyDescriptor mainMenuStripProperty = TypeDescriptor.GetProperties(parentForm)["MainMenuStrip"]; + if (mainMenuStripProperty != null && mainMenuStripProperty.GetValue(parentForm) == null) + { + mainMenuStripProperty.SetValue(parentForm, ToolStrip as MenuStrip); + } + } + } + + if (parentPanel != null) + { + if (!(ToolStrip is MenuStrip)) + { + PropertyDescriptor controlsProp = TypeDescriptor.GetProperties(parentPanel)["Controls"]; + + if (_componentChangeSvc != null) + { + _componentChangeSvc.OnComponentChanging(parentPanel, controlsProp); + } + + parentPanel.Join(ToolStrip, parentPanel.Rows.Length); + + if (_componentChangeSvc != null) + { + _componentChangeSvc.OnComponentChanged(parentPanel, controlsProp, parentPanel.Controls, parentPanel.Controls); + } + + //Try to fire ComponentChange on the Location Property for ToolStrip. + PropertyDescriptor locationProp = TypeDescriptor.GetProperties(ToolStrip)["Location"]; + if (_componentChangeSvc != null) + { + _componentChangeSvc.OnComponentChanging(ToolStrip, locationProp); + _componentChangeSvc.OnComponentChanged(ToolStrip, locationProp, null, null); + } + } + } + // If we are added to any container other than ToolStripPanel. + else if (parent != null) + { + // If we are adding the MenuStrip ... put it at the Last in the Controls Collection so it gets laid out first. + if (ToolStrip is MenuStrip) + { + int index = -1; + foreach (Control c in parent.Controls) + { + if (c is ToolStrip && (c != ToolStrip)) + { + index = parent.Controls.IndexOf(c); + } + } + if (index == -1) + { + // always place the toolStrip first. + index = parent.Controls.Count - 1; + } + parent.Controls.SetChildIndex(ToolStrip, index); + } + // If we are not a MenuStrip then we still need to be first to be laid out "after the menuStrip" + else + { + int index = -1; + foreach (Control c in parent.Controls) + { + // If we found an existing toolstrip (and not a menuStrip) then we can just return .. the base would have done correct parenting for us + MenuStrip menu = c as MenuStrip; + if (c is ToolStrip && menu == null) + { + return; + } + if (menu != null) + { + index = parent.Controls.IndexOf(c); + break; + } + } + if (index == -1) + { + // always place the toolStrip first. + index = parent.Controls.Count; + } + parent.Controls.SetChildIndex(ToolStrip, index - 1); + } + } + } + + /// + /// Setup the "AddNewItem" button + /// + private void InitializeNewItemDropDown() + { + if (!CanAddItems || !SupportEditing) + { + return; + } + ToolStrip toolStrip = (ToolStrip)Component; + AddNewTemplateNode(toolStrip); + // set up the right visibility state for the winbar. + SelSvc_SelectionChanged(null, EventArgs.Empty); + } + + /// + /// This is called to ascertain if the Glyph is totally visible. This is called from ToolStripMenuItemDesigner too. + /// + internal static bool IsGlyphTotallyVisible(Rectangle itemBounds, Rectangle parentBounds) + { + return parentBounds.Contains(itemBounds); + } + + /// + /// Returns true if the item is on the overflow. + /// + private bool ItemParentIsOverflow(ToolStripItem item) + { + ToolStripDropDown topmost = item.Owner as ToolStripDropDown; + if (topmost != null) + { + // walk back up the chain of windows to get the topmost + while (topmost != null && !(topmost is ToolStripOverflow)) + { + if (topmost.OwnerItem != null) + { + topmost = topmost.OwnerItem.GetCurrentParent() as ToolStripDropDown; + } + else + { + topmost = null; + } + } + } + return (topmost is ToolStripOverflow); + } + + /// + /// Sets up the add new button, and invalidates the behavior glyphs if needed so they always stay in sync. + /// + private void LayoutToolStrip() + { + if (!_disposed) + { + ToolStrip.PerformLayout(); + } + } + + internal static string NameFromText(string text, Type componentType, IServiceProvider serviceProvider, bool adjustCapitalization) + { + string name = NameFromText(text, componentType, serviceProvider); + if (adjustCapitalization) + { + string nameOfRandomItem = NameFromText(null, typeof(ToolStripMenuItem), + serviceProvider); + if (!string.IsNullOrEmpty(nameOfRandomItem) && char.IsUpper(nameOfRandomItem[0])) + { + name = char.ToUpper(name[0], CultureInfo.InvariantCulture) + name.Substring(1); + } + } + return name; + } + + /// + /// Computes a name from a text label by removing all spaces and non-alphanumeric characters. + /// + internal static string NameFromText(string text, Type componentType, IServiceProvider serviceProvider) + { + if (serviceProvider == null) + { + return null; + } + + INameCreationService nameCreate = serviceProvider.GetService(typeof(INameCreationService)) as INameCreationService; + IContainer container = (IContainer)serviceProvider.GetService(typeof(IContainer)); + string defaultName; + if (nameCreate != null && container != null) + { + defaultName = nameCreate.CreateName(container, componentType); + } + else + { + return null; + } + + Debug.Assert(defaultName != null && defaultName.Length > 0, "Couldn't create default name for item"); + + if (text == null || text.Length == 0 || text == "-") + { + return defaultName; + } + + string nameSuffix = componentType.Name; + // remove all the non letter and number characters. Append length of the item name... + System.Text.StringBuilder name = new System.Text.StringBuilder(text.Length + nameSuffix.Length); + bool nextCharToUpper = false; + for (int i = 0; i < text.Length; i++) + { + char c = text[i]; + if (nextCharToUpper) + { + if (char.IsLower(c)) + { + c = char.ToUpper(c, CultureInfo.CurrentCulture); + } + nextCharToUpper = false; + } + + if (char.IsLetterOrDigit(c)) + { + if (name.Length == 0) + { + if (char.IsDigit(c)) + { + // most languages don't allow a digit as the first char in an identifier. + continue; + } + + if (char.IsLower(c) != char.IsLower(defaultName[0])) + { + // match up the first char of the generated identifier with the case of the default. + if (char.IsLower(c)) + { + c = char.ToUpper(c, CultureInfo.CurrentCulture); + } + else + { + c = char.ToLower(c, CultureInfo.CurrentCulture); + } + } + } + name.Append(c); + } + else + { + if (char.IsWhiteSpace(c)) + { + nextCharToUpper = true; + } + } + } + + if (name.Length == 0) + { + return defaultName; + } + + name.Append(nameSuffix); + string baseName = name.ToString(); + + // verify we have a valid name. If not, start appending numbers if it matches one in the container. see if this name matches another one in the container.. + object existingComponent = container.Components[baseName]; + + if (existingComponent == null) + { + if (!nameCreate.IsValidName(baseName)) + { + // we don't have a name collision but this still isn't a valid name...something is wrong and we can't make a valid identifier out of this so bail. + return defaultName; + } + else + { + return baseName; + } + } + else + { + // start appending numbers. + string newName = baseName; + for (int indexer = 1; !nameCreate.IsValidName(newName) || container.Components[newName] != null; indexer++) + { + newName = baseName + indexer.ToString(CultureInfo.InvariantCulture); + } + return newName; + } + } + + /// + /// DesignerContextMenu should be shown when the ToolStripDesigner. + /// + protected override void OnContextMenu(int x, int y) + { + Component selComp = SelectionService.PrimarySelection as Component; + if (selComp is ToolStrip) + { + DesignerContextMenu.Show(x, y); + } + } + + protected override void OnDragEnter(DragEventArgs de) + { + base.OnDragEnter(de); + SetDragDropEffects(de); + } + + protected override void OnDragOver(DragEventArgs de) + { + base.OnDragOver(de); + SetDragDropEffects(de); + } + + /// + /// Add item on Drop and it its a MenuItem, open its dropDown. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + protected override void OnDragDrop(DragEventArgs de) + { + base.OnDragDrop(de); + // There is a "drop region" before firstItem which is not included in the "ToolStrip Item glyhs" so if the drop point falls in this drop region we should insert the items at the head instead of the tail of the toolStrip. + bool dropAtHead = false; + ToolStrip parentToolStrip = ToolStrip; + NativeMethods.POINT offset = new NativeMethods.POINT(de.X, de.Y); + NativeMethods.MapWindowPoints(IntPtr.Zero, parentToolStrip.Handle, offset, 1); + Point dropPoint = new Point(offset.x, offset.y); + if (ToolStrip.Orientation == Orientation.Horizontal) + { + if (ToolStrip.RightToLeft == RightToLeft.Yes) + { + if (dropPoint.X >= parentToolStrip.Items[0].Bounds.X) + { + dropAtHead = true; + } + } + else if (dropPoint.X <= parentToolStrip.Items[0].Bounds.X) + { + dropAtHead = true; + } + } + else + { + if (dropPoint.Y <= parentToolStrip.Items[0].Bounds.Y) + { + dropAtHead = true; + } + } + + if (de.Data is ToolStripItemDataObject data) + { + if (data.Owner == parentToolStrip) + { + string transDesc; + ArrayList components = data.DragComponents; + ToolStripItem primaryItem = data.PrimarySelection as ToolStripItem; + int primaryIndex = -1; + bool copy = (de.Effect == DragDropEffects.Copy); + + if (components.Count == 1) + { + string name = TypeDescriptor.GetComponentName(components[0]); + if (name == null || name.Length == 0) + { + name = components[0].GetType().Name; + } + transDesc = string.Format(copy ? SR.BehaviorServiceCopyControl : SR.BehaviorServiceMoveControl, name); + } + else + { + transDesc = string.Format(copy ? SR.BehaviorServiceCopyControls : SR.BehaviorServiceMoveControls, components.Count); + } + + // create a transaction so this happens as an atomic unit. + DesignerTransaction changeParent = _host.CreateTransaction(transDesc); + try + { + IComponentChangeService changeSvc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (changeSvc != null) + { + changeSvc.OnComponentChanging(parentToolStrip, TypeDescriptor.GetProperties(parentToolStrip)["Items"]); + } + + // If we are copying, then we want to make a copy of the components we are dragging + if (copy) + { + // Remember the primary selection if we had one + if (primaryItem != null) + { + primaryIndex = components.IndexOf(primaryItem); + } + if (KeyboardHandlingService != null) + { + KeyboardHandlingService.CopyInProgress = true; + } + components = DesignerUtils.CopyDragObjects(components, Component.Site) as ArrayList; + if (KeyboardHandlingService != null) + { + KeyboardHandlingService.CopyInProgress = false; + } + if (primaryIndex != -1) + { + primaryItem = components[primaryIndex] as ToolStripItem; + } + } + + if (de.Effect == DragDropEffects.Move || copy) + { + // Add the item. + for (int i = 0; i < components.Count; i++) + { + if (dropAtHead) + { + parentToolStrip.Items.Insert(0, components[i] as ToolStripItem); + } + else + { + parentToolStrip.Items.Add(components[i] as ToolStripItem); + } + } + + // show the dropDown for the primarySelection before the Drag-Drop operation started. + if (primaryItem is ToolStripDropDownItem primaryDropDownItem) + { + if (_host.GetDesigner(primaryDropDownItem) is ToolStripMenuItemDesigner dropDownItemDesigner) + { + dropDownItemDesigner.InitializeDropDown(); + } + } + + //Set the Selection .. + SelectionService.SetSelectedComponents(new IComponent[] { primaryItem }, SelectionTypes.Primary | SelectionTypes.Replace); + } + + if (changeSvc != null) + { + changeSvc.OnComponentChanged(parentToolStrip, TypeDescriptor.GetProperties(parentToolStrip)["Items"], null, null); + } + //fire extra changing/changed events so that the order is "restored" after undo/redo + if (copy) + { + if (changeSvc != null) + { + changeSvc.OnComponentChanging(parentToolStrip, TypeDescriptor.GetProperties(parentToolStrip)["Items"]); + changeSvc.OnComponentChanged(parentToolStrip, TypeDescriptor.GetProperties(parentToolStrip)["Items"], null, null); + } + } + // Refresh Glyphs... + BehaviorService.SyncSelection(); + } + + catch + { + if (changeParent != null) + { + changeParent.Cancel(); + changeParent = null; + } + + } + finally + { + if (changeParent != null) + { + changeParent.Commit(); + changeParent = null; + } + } + } + } + } + + + /// + /// Everytime we add Item .. the TemplateNode needs to go at the end if its not there. + /// + private void OnItemAdded(object sender, ToolStripItemEventArgs e) + { + if (_editorNode != null && (e.Item != _editorNode)) + { + int currentIndexOfEditor = ToolStrip.Items.IndexOf(_editorNode); + if (currentIndexOfEditor == -1 || currentIndexOfEditor != ToolStrip.Items.Count - 1) + { + // if the editor is not there or not at the end, add it to the end. + ToolStrip.ItemAdded -= new ToolStripItemEventHandler(OnItemAdded); + ToolStrip.SuspendLayout(); + ToolStrip.Items.Add(_editorNode); + ToolStrip.ResumeLayout(); + ToolStrip.ItemAdded += new ToolStripItemEventHandler(OnItemAdded); + } + } + LayoutToolStrip(); + } + + /// + /// Overriden so that the ToolStrip honors dragging only through container selector glyph. + /// + protected override void OnMouseDragMove(int x, int y) + { + if (!SelectionService.GetComponentSelected(ToolStrip)) + { + base.OnMouseDragMove(x, y); + } + } + + /// + /// Controls the dismissal of the drop down, here - we just cancel it + /// + private void OnOverflowDropDownClosing(object sender, ToolStripDropDownClosingEventArgs e) + { + //always dismiss this so we don't collapse the dropdown when the user clicks @ design time + e.Cancel = (e.CloseReason == ToolStripDropDownCloseReason.ItemClicked); + } + + + /// + /// Remove the Glyphs for Items on the overflow when the Overflow closes. + /// + private void OnOverFlowDropDownClosed(object sender, EventArgs e) + { + if (_toolStripAdornerWindowService != null && sender is ToolStripDropDownItem ddi) + { + _toolStripAdornerWindowService.Invalidate(ddi.DropDown.Bounds); + RemoveBodyGlyphsForOverflow(); + } + //select the last item on the parent toolStrip if the current selection is on the DropDown. + if (SelectionService.PrimarySelection is ToolStripItem curSel && curSel.IsOnOverflow) + { + ToolStripItem nextItem = ToolStrip.GetNextItem(ToolStrip.OverflowButton, ArrowDirection.Left); + if (nextItem != null) + { + SelectionService.SetSelectedComponents(new IComponent[] { nextItem }, SelectionTypes.Replace); + } + } + } + + /// + /// Add Glyphs when the OverFlow opens .... + /// + private void OnOverFlowDropDownOpened(object sender, EventArgs e) + { + //Show the TemplateNode + if (_editorNode != null) + { + _editorNode.Control.Visible = true; + _editorNode.Visible = true; + } + + ToolStripDropDownItem ddi = sender as ToolStripDropDownItem; + if (ddi != null) + { + RemoveBodyGlyphsForOverflow(); + AddBodyGlyphsForOverflow(); + } + //select the last item on the parent toolStrip if the current selection is on the DropDown. + if (!(SelectionService.PrimarySelection is ToolStripItem curSel) || (curSel != null && !curSel.IsOnOverflow)) + { + ToolStripItem nextItem = ddi.DropDown.GetNextItem(null, ArrowDirection.Down); + if (nextItem != null) + { + SelectionService.SetSelectedComponents(new IComponent[] { nextItem }, SelectionTypes.Replace); + BehaviorService.Invalidate(BehaviorService.ControlRectInAdornerWindow(ToolStrip)); + } + } + } + + /// + /// In Order to Draw the Selection Glyphs we need to reforce painting on the the AdonerWindow.This method forces the repaint + /// + private void OnOverFlowDropDownPaint(object sender, System.Windows.Forms.PaintEventArgs e) + { + foreach (ToolStripItem item in ToolStrip.Items) + { + if (item.Visible && item.IsOnOverflow && SelectionService.GetComponentSelected(item)) + { + if (_host.GetDesigner(item) is ToolStripItemDesigner designer) + { + Rectangle r = designer.GetGlyphBounds(); + ToolStripDesignerUtils.GetAdjustedBounds(item, ref r); + r.Inflate(GLYPHBORDER, GLYPHBORDER); + //this will allow any Glyphs to re-paint + //after this control and its designer has painted + BehaviorService b = (BehaviorService)GetService(typeof(BehaviorService)); + if (b != null) + { + b.ProcessPaintMessage(r); + } + } + } + } + } + + /// + /// Change the parent of the overFlow so that it is parented to the ToolStripAdornerWindow + /// + private void OnOverFlowDropDownOpening(object sender, EventArgs e) + { + ToolStripDropDownItem ddi = sender as ToolStripDropDownItem; + if (ddi.DropDown.TopLevel) + { + ddi.DropDown.TopLevel = false; + } + + if (_toolStripAdornerWindowService != null) + { + ToolStrip.SuspendLayout(); + ddi.DropDown.Parent = _toolStripAdornerWindowService.ToolStripAdornerWindowControl; + ToolStrip.ResumeLayout(); + } + } + + /// + /// When Items change the size, Recalculate the glyph sizes. + /// + private void OnOverflowDropDownResize(object sender, EventArgs e) + { + ToolStripDropDown dropDown = sender as ToolStripDropDown; + if (dropDown.Visible) + { + // Re-Add the Glyphs to refresh the bounds... and Add new ones if new items get pushed into the OverFlow. + RemoveBodyGlyphsForOverflow(); + AddBodyGlyphsForOverflow(); + } + + if (_toolStripAdornerWindowService != null && dropDown != null) + { + _toolStripAdornerWindowService.Invalidate(); + } + } + + /// + /// Set proper cursor + /// + protected override void OnSetCursor() + { + if (_toolboxService == null) + { + _toolboxService = (IToolboxService)GetService(typeof(IToolboxService)); + } + + if (_toolboxService == null || !_toolboxService.SetCursor() || InheritanceAttribute.Equals(InheritanceAttribute.InheritedReadOnly)) + { + Cursor.Current = Cursors.Default; + } + } + + /// + /// ResumeLayout after Undone. + /// + private void OnUndone(object source, EventArgs e) + { + /// IMPORTANT : The Undo Unit .. Clears of the ITems.... + if (_editorNode != null && (ToolStrip.Items.IndexOf(_editorNode) == -1)) + { + ToolStrip.Items.Add(_editorNode); + } + if (_undoingCalled) + { + // StatusStrip required a ResumeLayout and then a performLayout... So that the Layout is proper after any user-transaction UNDONE. + ToolStrip.ResumeLayout(true/*performLayout*/); + ToolStrip.PerformLayout(); + // ReInitialize the Glyphs after Layout is resumed !! + if (SelectionService.PrimarySelection is ToolStripDropDownItem selectedItem) + { + if (_host.GetDesigner(selectedItem) is ToolStripMenuItemDesigner selectedItemDesigner) + { + selectedItemDesigner.InitializeBodyGlyphsForItems(false, selectedItem); + selectedItemDesigner.InitializeBodyGlyphsForItems(true, selectedItem); + } + } + _undoingCalled = false; + } + BehaviorService.SyncSelection(); + } + + /// + /// SuspendLayout before unDoing. + /// + private void OnUndoing(object source, EventArgs e) + { + if (CheckIfItemSelected() || SelectionService.GetComponentSelected(ToolStrip)) + { + _undoingCalled = true; + ToolStrip.SuspendLayout(); + } + } + + /// + /// SyncSelection on ToolStrip move. + /// + private void OnToolStripMove(object sender, System.EventArgs e) + { + if (SelectionService.GetComponentSelected(ToolStrip)) + { + BehaviorService.SyncSelection(); + } + } + + /// + /// Remove all the glyphs we were are not visible.. + /// + private void OnToolStripVisibleChanged(object sender, System.EventArgs e) + { + if (sender is ToolStrip tool && !tool.Visible) + { + SelectionManager selMgr = (SelectionManager)GetService(typeof(SelectionManager)); + Glyph[] currentBodyGlyphs = new Glyph[selMgr.BodyGlyphAdorner.Glyphs.Count]; + selMgr.BodyGlyphAdorner.Glyphs.CopyTo(currentBodyGlyphs, 0); + //Remove the ToolStripItemGlyphs. + foreach (Glyph g in currentBodyGlyphs) + { + if (g is ToolStripItemGlyph) + { + selMgr.BodyGlyphAdorner.Glyphs.Remove(g); + } + } + } + } + + /// + /// Allows a designer to filter the set of properties the component it is designing will expose through the TypeDescriptor object. This method is called immediately before its corresponding "Post" method. If you are overriding this method you should call the base implementation before you perform your own filtering. + /// + protected override void PreFilterProperties(IDictionary properties) + { + base.PreFilterProperties(properties); + PropertyDescriptor prop; + string[] shadowProps = new string[] { + "Visible", + "AllowDrop", + "AllowItemReorder" + }; + Attribute[] empty = new Attribute[0]; + for (int i = 0; i < shadowProps.Length; i++) + { + prop = (PropertyDescriptor)properties[shadowProps[i]]; + if (prop != null) + { + properties[shadowProps[i]] = TypeDescriptor.CreateProperty(typeof(ToolStripDesigner), prop, empty); + } + } + } + + /// + /// Remove the glyphs for individual items on the DropDown. + /// + private void RemoveBodyGlyphsForOverflow() + { + // now walk the winbar and add glyphs for each of it's children + foreach (ToolStripItem item in ToolStrip.Items) + { + if (item is DesignerToolStripControlHost) + { + continue; + } + // make sure it's on the Overflow... + if (item.Placement == ToolStripItemPlacement.Overflow) + { + ToolStripItemDesigner dropDownItemDesigner = (ToolStripItemDesigner)_host.GetDesigner(item); + if (dropDownItemDesigner != null) + { + ControlBodyGlyph glyph = dropDownItemDesigner.bodyGlyph; + if (glyph != null && _toolStripAdornerWindowService != null && _toolStripAdornerWindowService.DropDownAdorner.Glyphs.Contains(glyph)) + { + _toolStripAdornerWindowService.DropDownAdorner.Glyphs.Remove(glyph); + } + } + } + } + } + + // + // IMPORTANT FUNCTION: THIS IS CALLED FROM THE ToolStripItemGlyph to rollback + // the TemplateNode Edition on the Parent ToolStrip. + // + internal void RollBack() + { + if (_tn != null) + { + _tn.RollBack(); + _editorNode.Width = _tn.EditorToolStrip.Width; + } + } + + // + // Resets the ToolStrip Visible to be the default value + // + private void ResetVisible() + { + Visible = true; + } + + /// + /// When the Drag Data does not contain ToolStripItem; change the dragEffect to None; This will result current cursor to change into NO-SMOKING cursor + /// + private void SetDragDropEffects(DragEventArgs de) + { + if (de.Data is ToolStripItemDataObject data) + { + if (data.Owner != ToolStrip) + { + de.Effect = DragDropEffects.None; + } + else + { + de.Effect = (Control.ModifierKeys == Keys.Control) ? DragDropEffects.Copy : DragDropEffects.Move; + } + } + } + + /// + /// When selection changes to the winbar, show the "AddItemsButton", when it leaves, hide it. + /// + private void SelSvc_SelectionChanging(object sender, EventArgs e) + { + if (_toolStripSelected) + { + // first commit the node + if (_tn != null && _tn.Active) + { + _tn.Commit(false, false); + } + } + + bool showToolStrip = CheckIfItemSelected(); + //Check All the SelectedComponents to find is toolstrips are selected + if (!showToolStrip && !SelectionService.GetComponentSelected(ToolStrip)) + { + ToolStrip.Visible = _currentVisible; + if (!_currentVisible && _parentNotVisible) + { + ToolStrip.Parent.Visible = _currentVisible; + _parentNotVisible = false; + } + if (ToolStrip.OverflowButton.DropDown.Visible) + { + ToolStrip.OverflowButton.HideDropDown(); + } + //Always Hide the EditorNode if the ToolStrip Is Not Selected... + if (_editorNode != null) + { + _editorNode.Visible = false; + } + // Show Hide Items... + ShowHideToolStripItems(false); + _toolStripSelected = false; + } + } + + /// + /// When selection changes to the winbar, show the "AddItemsButton", when it leaves, hide it. + /// + private void SelSvc_SelectionChanged(object sender, EventArgs e) + { + if (_miniToolStrip != null && _host != null) + { + bool showToolStrip = false; + bool itemSelected = CheckIfItemSelected(); + showToolStrip = itemSelected || SelectionService.GetComponentSelected(ToolStrip); + //Check All the SelectedComponents to find is toolstrips are selected + if (showToolStrip) + { + // If now the ToolStrip is selected,, Hide its Overflow + + if (SelectionService.GetComponentSelected(ToolStrip)) + { + if (!DontCloseOverflow && ToolStrip.OverflowButton.DropDown.Visible) + { + ToolStrip.OverflowButton.HideDropDown(); + } + } + + // Show Hide Items... + ShowHideToolStripItems(true); + if (!_currentVisible || !Control.Visible) + { + // Since the control wasn't visible make it visible + Control.Visible = true; + // make the current parent visible too. + if (ToolStrip.Parent is ToolStripPanel && !ToolStrip.Parent.Visible) + { + _parentNotVisible = true; + ToolStrip.Parent.Visible = true; + } + // Since the GetBodyGlyphs is called before we come here In this case where the ToolStrip is going from visible==false to visible==true we need to re-add the glyphs for the items. + BehaviorService.SyncSelection(); + } + //Always Show the EditorNode if the ToolStripIsSelected and is PrimarySelection or one of item is selected. + if (_editorNode != null && (SelectionService.PrimarySelection == ToolStrip || itemSelected)) + { + bool originalSyncSelection = FireSyncSelection; + ToolStripPanel parent = ToolStrip.Parent as ToolStripPanel; + try + { + if (parent != null) + { + parent.LocationChanged += new System.EventHandler(OnToolStripMove); + } + FireSyncSelection = true; + _editorNode.Visible = true; + } + finally + { + FireSyncSelection = originalSyncSelection; + if (parent != null) + { + parent.LocationChanged -= new System.EventHandler(OnToolStripMove); + } + } + } + + // REQUIRED FOR THE REFRESH OF GLYPHS BUT TRY TO BE SMART ABOUT THE REGION TO INVALIDATE.... + if (!(SelectionService.PrimarySelection is ToolStripItem selectedItem)) + { + if (KeyboardHandlingService != null) + { + selectedItem = KeyboardHandlingService.SelectedDesignerControl as ToolStripItem; + } + } + _toolStripSelected = true; + } + } + } + + // + // Determines when should the Visible property be serialized. + // + private bool ShouldSerializeVisible() => !Visible; + + // + // Determines when should the AllowDrop property be serialized. + // + private bool ShouldSerializeAllowDrop() => (bool)ShadowProperties["AllowDrop"]; + + // + // Determines when should the AllowItemReorder property be serialized. + // + private bool ShouldSerializeAllowItemReorder() => (bool)ShadowProperties["AllowItemReorder"]; + + // + // This is the method that gets called when the Designer has to show thwe InSitu Edit Node, + // + internal void ShowEditNode(bool clicked) + { + // SPECIAL LOGIC TO MIMIC THE MAINMENU BEHAVIOR.. PUSH THE TEMPLATE NODE and ADD A MENUITEM HERE... + if (ToolStrip is MenuStrip) + { + // The TemplateNode should no longer be selected. + if (KeyboardHandlingService != null) + { + KeyboardHandlingService.ResetActiveTemplateNodeSelectionState(); + } + try + { + ToolStripItem newItem = AddNewItem(typeof(ToolStripMenuItem)); + if (newItem != null) + { + if (_host.GetDesigner(newItem) is ToolStripItemDesigner newItemDesigner) + { + newItemDesigner.dummyItemAdded = true; + ((ToolStripMenuItemDesigner)newItemDesigner).InitializeDropDown(); + try + { + _addingDummyItem = true; + newItemDesigner.ShowEditNode(clicked); + } + finally + { + _addingDummyItem = false; + } + } + } + } + catch (InvalidOperationException ex) + { + Debug.Assert(NewItemTransaction == null, "NewItemTransaction should have been nulled out and cancelled by now."); + IUIService uiService = (IUIService)GetService(typeof(IUIService)); + uiService.ShowError(ex.Message); + + if (KeyboardHandlingService != null) + { + KeyboardHandlingService.ResetActiveTemplateNodeSelectionState(); + } + } + } + } + + //Helper function to toggle the Item Visibility + private void ShowHideToolStripItems(bool toolStripSelected) + { + //If we arent Selected then turn the TOPLEVEL ITEMS visibility WYSIWYG + foreach (ToolStripItem item in ToolStrip.Items) + { + if (item is DesignerToolStripControlHost) + { + continue; + } + // Get the itemDesigner... + ToolStripItemDesigner itemDesigner = (ToolStripItemDesigner)_host.GetDesigner(item); + if (itemDesigner != null) + { + itemDesigner.SetItemVisible(toolStripSelected, this); + } + } + if (FireSyncSelection) + { + BehaviorService.SyncSelection(); + FireSyncSelection = false; + } + } + + // this is required when addition of TemplateNode causes the toolStrip to Layout .. E.g : Spring ToolStripStatusLabel. + private void ToolStrip_LayoutCompleted(object sender, EventArgs e) + { + if (FireSyncSelection) + { + BehaviorService.SyncSelection(); + } + } + + /// + /// Make sure the AddItem button stays in the right spot. + /// + private void ToolStrip_Resize(object sender, EventArgs e) + { + if (!_addingDummyItem && !_disposed && (CheckIfItemSelected() || SelectionService.GetComponentSelected(ToolStrip))) + { + if (_miniToolStrip != null && _miniToolStrip.Visible) + { + LayoutToolStrip(); + } + BehaviorService.SyncSelection(); + } + } + + /// + /// Handle lower level mouse input. + /// + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_CONTEXTMENU: + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + bool inBounds = GetHitTest(new Point(x, y)); + if (inBounds) + { + return; + } + base.WndProc(ref m); + break; + case NativeMethods.WM_LBUTTONDOWN: + case NativeMethods.WM_RBUTTONDOWN: + // commit any insitu if any... + Commit(); + base.WndProc(ref m); + break; + default: + base.WndProc(ref m); + break; + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesignerUtils.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesignerUtils.cs new file mode 100644 index 00000000000..49a0d1c16d2 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesignerUtils.cs @@ -0,0 +1,655 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Design; +using System.Reflection; +using System.Security; +using System.Security.Permissions; +using System.Windows.Forms.Design.Behavior; +using Microsoft.Win32; + +namespace System.Windows.Forms.Design +{ + internal sealed class ToolStripDesignerUtils + { + private static readonly Type s_toolStripItemType = typeof(ToolStripItem); + [ThreadStatic] + private static Dictionary s_cachedToolboxItems; + [ThreadStatic] + private static int s_customToolStripItemCount = 0; + private const int TOOLSTRIPCHARCOUNT = 9; + // used to cache in the selection. This is used when the selection is changing and we need to invalidate the original selection Especially, when the selection is changed through NON UI designer action like through propertyGrid or through Doc Outline. + public static ArrayList originalSelComps; + + [ThreadStatic] + private static Dictionary s_cachedWinformsImages; + private static string s_systemWindowsFormsNamespace = typeof(ToolStripItem).Namespace; + + private ToolStripDesignerUtils() + { + } + + #region NewItemTypeLists + // This section controls the ordering of standard item types in all the various pieces of toolstrip designer UI. + // ToolStrip - Default item is determined by being first in the list + private static readonly Type[] s_newItemTypesForToolStrip = + new Type[]{typeof(ToolStripButton), typeof(ToolStripLabel), typeof(ToolStripSplitButton), typeof(ToolStripDropDownButton), typeof(ToolStripSeparator), typeof(ToolStripComboBox), typeof(ToolStripTextBox), typeof(ToolStripProgressBar)}; + // StatusStrip - Default item is determined by being first in the list + private static readonly Type[] s_newItemTypesForStatusStrip = + new Type[]{typeof(ToolStripStatusLabel), typeof(ToolStripProgressBar), typeof(ToolStripDropDownButton), typeof(ToolStripSplitButton)}; + // MenuStrip - Default item is determined by being first in the list + private static readonly Type[] s_newItemTypesForMenuStrip = + new Type[]{typeof(ToolStripMenuItem), typeof(ToolStripComboBox), typeof(ToolStripTextBox)}; + // ToolStripDropDown - roughly same as menu strip. + private static readonly Type[] s_newItemTypesForToolStripDropDownMenu = + new Type[]{typeof(ToolStripMenuItem), typeof(ToolStripComboBox), typeof(ToolStripSeparator), typeof(ToolStripTextBox)}; + #endregion + + // Get the Correct bounds for painting... + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + public static void GetAdjustedBounds(ToolStripItem item, ref Rectangle r) + { + // adjust bounds as per item + if (!(item is ToolStripControlHost && item.IsOnDropDown)) + { + //Deflate the SelectionGlyph for the MenuItems... + if (item is ToolStripMenuItem && item.IsOnDropDown) + { + r.Inflate(-3, -2); + r.Width++; + } + else if (item is ToolStripControlHost && !item.IsOnDropDown) + { + r.Inflate(0, -2); + } + else if (item is ToolStripMenuItem && !item.IsOnDropDown) + { + r.Inflate(-3, -3); + } + else + { + r.Inflate(-1, -1); + } + } + } + + /// + /// If IComponent is ToolStrip return ToolStrip + /// If IComponent is ToolStripItem return the Owner ToolStrip + /// If IComponent is ToolStripDropDownItem return the child DropDown ToolStrip + /// + private static ToolStrip GetToolStripFromComponent(IComponent component) + { + ToolStrip parent; + if (component is ToolStripItem stripItem) + { + if (!(stripItem is ToolStripDropDownItem)) + { + parent = stripItem.Owner; + } + else + { + parent = ((ToolStripDropDownItem)stripItem).DropDown; + } + } + else + { + parent = component as ToolStrip; + } + return parent; + } + + private static ToolboxItem GetCachedToolboxItem(Type itemType) + { + ToolboxItem tbxItem = null; + if (s_cachedToolboxItems == null) + { + s_cachedToolboxItems = new Dictionary(); + } + else if (s_cachedToolboxItems.ContainsKey(itemType)) + { + tbxItem = s_cachedToolboxItems[itemType]; + return tbxItem; + } + + // no cache hit - load the item. + if (tbxItem == null) + { + // create a toolbox item to match + tbxItem = new ToolboxItem(itemType); + } + + s_cachedToolboxItems[itemType] = tbxItem; + if (s_customToolStripItemCount > 0 && (s_customToolStripItemCount * 2 < s_cachedToolboxItems.Count)) + { + // time to clear the toolbox item cache - we've got twice the number of toolbox items than actual custom item types. + s_cachedToolboxItems.Clear(); + } + return tbxItem; + } + + // only call this for well known items. + private static Bitmap GetKnownToolboxBitmap(Type itemType) + { + if (s_cachedWinformsImages == null) + { + s_cachedWinformsImages = new Dictionary(); + } + if (!s_cachedWinformsImages.ContainsKey(itemType)) + { + Bitmap knownImage = ToolboxBitmapAttribute.GetImageFromResource(itemType, null, false) as Bitmap; + s_cachedWinformsImages[itemType] = knownImage; + return knownImage; + } + return s_cachedWinformsImages[itemType]; + } + + public static Bitmap GetToolboxBitmap(Type itemType) + { + // if and only if the namespace of the type is System.Windows.Forms. try to pull the image out of the manifest. + if (itemType.Namespace == s_systemWindowsFormsNamespace) + { + return GetKnownToolboxBitmap(itemType); + } + + // check to see if we've got a toolbox item, and use it. + ToolboxItem tbxItem = GetCachedToolboxItem(itemType); + if (tbxItem != null) + { + return tbxItem.Bitmap; + } + // if all else fails, throw up a default image. + return GetKnownToolboxBitmap(typeof(Component)); + } + + /// + /// Fishes out the display name attribute from the Toolbox item if not present, uses Type.Name + /// + public static string GetToolboxDescription(Type itemType) + { + string currentName = null; + ToolboxItem tbxItem = GetCachedToolboxItem(itemType); + if (tbxItem != null) + { + currentName = tbxItem.DisplayName; + } + if (currentName == null) + { + currentName = itemType.Name; + } + if (currentName.StartsWith("ToolStrip")) + { + return currentName.Substring(TOOLSTRIPCHARCOUNT); + } + return currentName; + } + + + /// + /// The first item returned should be the DefaultItem to create on the ToolStrip + /// + public static Type[] GetStandardItemTypes(IComponent component) + { + ToolStrip toolStrip = GetToolStripFromComponent(component); + if (toolStrip is MenuStrip) + { + return s_newItemTypesForMenuStrip; + } + else if (toolStrip is ToolStripDropDownMenu) + { + // ToolStripDropDown gets default items. + return s_newItemTypesForToolStripDropDownMenu; + } + else if (toolStrip is StatusStrip) + { + return s_newItemTypesForStatusStrip; + } + Debug.Assert(toolStrip != null, "why werent we handed a toolstrip here? returning default list"); + return s_newItemTypesForToolStrip; + } + + private static ToolStripItemDesignerAvailability GetDesignerVisibility(ToolStrip toolStrip) + { + ToolStripItemDesignerAvailability visiblity; + if (toolStrip is StatusStrip) + { + visiblity = ToolStripItemDesignerAvailability.StatusStrip; + } + else if (toolStrip is MenuStrip) + { + visiblity = ToolStripItemDesignerAvailability.MenuStrip; + } + else if (toolStrip is ToolStripDropDownMenu) + { + visiblity = ToolStripItemDesignerAvailability.ContextMenuStrip; + } + else + { + visiblity = ToolStripItemDesignerAvailability.ToolStrip; + } + return visiblity; + } + + + public static Type[] GetCustomItemTypes(IComponent component, IServiceProvider serviceProvider) + { + ITypeDiscoveryService discoveryService = null; + if (serviceProvider != null) + { + discoveryService = serviceProvider.GetService(typeof(ITypeDiscoveryService)) as ITypeDiscoveryService; + } + return GetCustomItemTypes(component, discoveryService); + } + public static Type[] GetCustomItemTypes(IComponent component, ITypeDiscoveryService discoveryService) + { + if (discoveryService != null) + { + // fish out all types which derive from toolstrip item + ICollection itemTypes = discoveryService.GetTypes(s_toolStripItemType, false /*excludeGlobalTypes*/); + ToolStrip toolStrip = GetToolStripFromComponent(component); + // determine the value of the visibility attribute which matches the current toolstrip type. + ToolStripItemDesignerAvailability currentToolStripVisibility = GetDesignerVisibility(toolStrip); + Debug.Assert(currentToolStripVisibility != ToolStripItemDesignerAvailability.None, "Why is GetDesignerVisibility returning None?"); + // fish out the ones we've already listed. + Type[] stockItemTypeList = GetStandardItemTypes(component); + if (currentToolStripVisibility != ToolStripItemDesignerAvailability.None) + { + ArrayList createableTypes = new ArrayList(itemTypes.Count); + foreach (Type t in itemTypes) + { + if (t.IsAbstract) + { + continue; + } + if (!t.IsPublic && !t.IsNestedPublic) + { + continue; + } + if (t.ContainsGenericParameters) + { + continue; + } + // Check if we have public constructor... + ConstructorInfo ctor = t.GetConstructor(new Type[0]); + if (ctor == null) + { + continue; + } + + // if the visibility matches the current toolstrip type, add it to the list of possible types to create. + ToolStripItemDesignerAvailabilityAttribute visiblityAttribute = (ToolStripItemDesignerAvailabilityAttribute)TypeDescriptor.GetAttributes(t)[typeof(ToolStripItemDesignerAvailabilityAttribute)]; + if (visiblityAttribute != null && ((visiblityAttribute.ItemAdditionVisibility & currentToolStripVisibility) == currentToolStripVisibility)) + { + bool isStockType = false; + // PERF: consider a dictionary - but this list will usually be 3-7 items. + foreach (Type stockType in stockItemTypeList) + { + if (stockType == t) + { + isStockType = true; + break; + } + } + if (!isStockType) + { + createableTypes.Add(t); + } + } + } + + if (createableTypes.Count > 0) + { + Type[] createableTypesArray = new Type[createableTypes.Count]; + createableTypes.CopyTo(createableTypesArray, 0); + s_customToolStripItemCount = createableTypes.Count; + return createableTypesArray; + } + } + } + s_customToolStripItemCount = 0; + return new Type[0]; + } + + /// + /// wraps the result of GetStandardItemTypes in ItemTypeToolStripMenuItems. + /// + public static ToolStripItem[] GetStandardItemMenuItems(IComponent component, EventHandler onClick, bool convertTo) + { + Type[] standardTypes = GetStandardItemTypes(component); + ToolStripItem[] items = new ToolStripItem[standardTypes.Length]; + for (int i = 0; i < standardTypes.Length; i++) + { + ItemTypeToolStripMenuItem item = new ItemTypeToolStripMenuItem(standardTypes[i]) + { + ConvertTo = convertTo + }; + if (onClick != null) + { + item.Click += onClick; + } + items[i] = item; + } + return items; + } + + /// + /// wraps the result of GetCustomItemTypes in ItemTypeToolStripMenuItems. + /// + public static ToolStripItem[] GetCustomItemMenuItems(IComponent component, EventHandler onClick, bool convertTo, IServiceProvider serviceProvider) + { + Type[] customTypes = GetCustomItemTypes(component, serviceProvider); + ToolStripItem[] items = new ToolStripItem[customTypes.Length]; + for (int i = 0; i < customTypes.Length; i++) + { + ItemTypeToolStripMenuItem item = new ItemTypeToolStripMenuItem(customTypes[i]) + { + ConvertTo = convertTo + }; + if (onClick != null) + { + item.Click += onClick; + } + items[i] = item; + } + return items; + } + + /// + /// build up a list of standard items separated by the custom items + /// + + public static NewItemsContextMenuStrip GetNewItemDropDown(IComponent component, ToolStripItem currentItem, EventHandler onClick, bool convertTo, IServiceProvider serviceProvider, bool populateCustom) + { + NewItemsContextMenuStrip contextMenu = new NewItemsContextMenuStrip(component, currentItem, onClick, convertTo, serviceProvider); + contextMenu.GroupOrdering.Add("StandardList"); + contextMenu.GroupOrdering.Add("CustomList"); + // plumb through the standard and custom items. + foreach (ToolStripItem item in GetStandardItemMenuItems(component, onClick, convertTo)) + { + contextMenu.Groups["StandardList"].Items.Add(item); + if (convertTo) + { + if (item is ItemTypeToolStripMenuItem toolItem && currentItem != null && toolItem.ItemType == currentItem.GetType()) + { + toolItem.Enabled = false; + } + } + } + if (populateCustom) + { + GetCustomNewItemDropDown(contextMenu, component, currentItem, onClick, convertTo, serviceProvider); + } + if (serviceProvider.GetService(typeof(IUIService)) is IUIService uis) + { + contextMenu.Renderer = (ToolStripProfessionalRenderer)uis.Styles["VsRenderer"]; + contextMenu.Font = (Font)uis.Styles["DialogFont"]; + if (uis.Styles["VsColorPanelText"] is Color) + { + contextMenu.ForeColor = (Color)uis.Styles["VsColorPanelText"]; + } + } + contextMenu.Populate(); + return contextMenu; + } + + public static void GetCustomNewItemDropDown(NewItemsContextMenuStrip contextMenu, IComponent component, ToolStripItem currentItem, EventHandler onClick, bool convertTo, IServiceProvider serviceProvider) + { + foreach (ToolStripItem item in GetCustomItemMenuItems(component, onClick, convertTo, serviceProvider)) + { + contextMenu.Groups["CustomList"].Items.Add(item); + if (convertTo) + { + if (item is ItemTypeToolStripMenuItem toolItem && currentItem != null && toolItem.ItemType == currentItem.GetType()) + { + toolItem.Enabled = false; + } + } + } + contextMenu.Populate(); + } + + public static void InvalidateSelection(ArrayList originalSelComps, ToolStripItem nextSelection, IServiceProvider provider, bool shiftPressed) + { + // if we are not selecting a ToolStripItem then return (dont invalidate). + if (nextSelection == null || provider == null) + { + return; + } + //InvalidateOriginal SelectedComponents. + Region invalidateRegion = null; + Region itemRegion = null; + int GLYPHBORDER = 1; + int GLYPHINSET = 2; + ToolStripItemDesigner designer = null; + bool templateNodeSelected = false; + + try + { + Rectangle invalidateBounds = Rectangle.Empty; + IDesignerHost designerHost = (IDesignerHost)provider.GetService(typeof(IDesignerHost)); + + if (designerHost != null) + { + foreach (Component comp in originalSelComps) + { + if (comp is ToolStripItem selItem) + { + if ((originalSelComps.Count > 1) || + (originalSelComps.Count == 1 && selItem.GetCurrentParent() != nextSelection.GetCurrentParent()) || + selItem is ToolStripSeparator || selItem is ToolStripControlHost || !selItem.IsOnDropDown || selItem.IsOnOverflow) + { + // finally Invalidate the selection rect ... + designer = designerHost.GetDesigner(selItem) as ToolStripItemDesigner; + if (designer != null) + { + invalidateBounds = designer.GetGlyphBounds(); + GetAdjustedBounds(selItem, ref invalidateBounds); + invalidateBounds.Inflate(GLYPHBORDER, GLYPHBORDER); + + if (invalidateRegion == null) + { + invalidateRegion = new Region(invalidateBounds); + invalidateBounds.Inflate(-GLYPHINSET, -GLYPHINSET); + invalidateRegion.Exclude(invalidateBounds); + } + else + { + itemRegion = new Region(invalidateBounds); + invalidateBounds.Inflate(-GLYPHINSET, -GLYPHINSET); + itemRegion.Exclude(invalidateBounds); + invalidateRegion.Union(itemRegion); + } + } + } + } + } + } + + if (invalidateRegion != null || templateNodeSelected || shiftPressed) + { + BehaviorService behaviorService = (BehaviorService)provider.GetService(typeof(BehaviorService)); + if (behaviorService != null) + { + if (invalidateRegion != null) + { + behaviorService.Invalidate(invalidateRegion); + } + + // When a ToolStripItem is PrimarySelection, the glyph bounds are not invalidated through the SelectionManager so we have to do this. + designer = designerHost.GetDesigner(nextSelection) as ToolStripItemDesigner; + if (designer != null) + { + invalidateBounds = designer.GetGlyphBounds(); + GetAdjustedBounds(nextSelection, ref invalidateBounds); + invalidateBounds.Inflate(GLYPHBORDER, GLYPHBORDER); + invalidateRegion = new Region(invalidateBounds); + + invalidateBounds.Inflate(-GLYPHINSET, -GLYPHINSET); + invalidateRegion.Exclude(invalidateBounds); + behaviorService.Invalidate(invalidateRegion); + } + } + } + } + finally + { + if (invalidateRegion != null) + { + invalidateRegion.Dispose(); + } + + if (itemRegion != null) + { + itemRegion.Dispose(); + } + } + } + + /// represents cached information about the display + internal static class DisplayInformation + { + private static bool s_highContrast; //whether we are under hight contrast mode + private static bool s_lowRes; //whether we are under low resolution mode + private static bool s_isTerminalServerSession; //whether this application is run on a terminal server (remote desktop) + private static bool s_highContrastSettingValid; //indicates whether the high contrast setting is correct + private static bool s_lowResSettingValid; //indicates whether the low resolution setting is correct + private static bool s_terminalSettingValid; //indicates whether the terminal server setting is correct + private static short s_bitsPerPixel; + private static bool s_dropShadowSettingValid; + private static bool s_dropShadowEnabled; + + + static DisplayInformation() + { + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(UserPreferenceChanged); + SystemEvents.DisplaySettingsChanged += new EventHandler(DisplaySettingChanged); + } + + public static short BitsPerPixel + { + get + { + if (s_bitsPerPixel == 0) + { + new EnvironmentPermission(PermissionState.Unrestricted).Assert(); + try + { + foreach (Screen s in Screen.AllScreens) + { + if (s_bitsPerPixel == 0) + { + s_bitsPerPixel = (short)s.BitsPerPixel; + } + else + { + s_bitsPerPixel = (short)Math.Min(s.BitsPerPixel, s_bitsPerPixel); + } + } + } + finally + { + CodeAccessPermission.RevertAssert(); + } + } + return s_bitsPerPixel; + } + } + + /// + ///tests to see if the monitor is in low resolution mode (8-bit color depth or less). + /// + public static bool LowResolution + { + get + { + if (s_lowResSettingValid) + { + return s_lowRes; + } + s_lowRes = BitsPerPixel <= 8; + s_lowResSettingValid = true; + return s_lowRes; + } + } + + /// + ///tests to see if we are under high contrast mode + /// + public static bool HighContrast + { + get + { + if (s_highContrastSettingValid) + { + return s_highContrast; + } + s_highContrast = SystemInformation.HighContrast; + s_highContrastSettingValid = true; + return s_highContrast; + } + } + public static bool IsDropShadowEnabled + { + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + get + { + if (s_dropShadowSettingValid) + { + return s_dropShadowEnabled; + } + s_dropShadowEnabled = SystemInformation.IsDropShadowEnabled; + s_dropShadowSettingValid = true; + return s_dropShadowEnabled; + } + } + + /// + ///test to see if we are under terminal server mode + /// + public static bool TerminalServer + { + get + { + if (s_terminalSettingValid) + { + return s_isTerminalServerSession; + } + s_isTerminalServerSession = SystemInformation.TerminalServerSession; + s_terminalSettingValid = true; + return s_isTerminalServerSession; + } + } + + /// + ///event handler for change in display setting + /// + private static void DisplaySettingChanged(object obj, EventArgs ea) + { + s_highContrastSettingValid = false; + s_lowResSettingValid = false; + s_terminalSettingValid = false; + s_dropShadowSettingValid = false; + } + + /// + ///event handler for change in user preference + /// + private static void UserPreferenceChanged(object obj, UserPreferenceChangedEventArgs ea) + { + s_highContrastSettingValid = false; + s_lowResSettingValid = false; + s_terminalSettingValid = false; + s_dropShadowSettingValid = false; + s_bitsPerPixel = 0; + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDropDownDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDropDownDesigner.cs new file mode 100644 index 00000000000..8e13fc72814 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDropDownDesigner.cs @@ -0,0 +1,784 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Configuration; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Globalization; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// Designer for ToolStripDropDowns...just provides the Edit... verb. + /// + internal class ToolStripDropDownDesigner : ComponentDesigner + { + private ISelectionService selSvc; + private MenuStrip designMenu; + private ToolStripMenuItem menuItem; + private IDesignerHost host; + private ToolStripDropDown dropDown; + private bool selected; + private ControlBodyGlyph dummyToolStripGlyph; + private uint _editingCollection = 0; // non-zero if the collection editor is up for this winbar or a child of it. + MainMenu parentMenu = null; + FormDocumentDesigner parentFormDesigner = null; + internal ToolStripMenuItem currentParent = null; + private INestedContainer nestedContainer = null; //NestedContainer for our DesignTime MenuItem. + private UndoEngine undoEngine = null; + + /// + /// ShadowProperty. + /// + private bool AutoClose + { + get => (bool)ShadowProperties["AutoClose"]; + set => ShadowProperties["AutoClose"] = value; + } + + private bool AllowDrop + { + get => (bool)ShadowProperties["AllowDrop"]; + set => ShadowProperties["AllowDrop"] = value; + } + + /// + /// Adds designer actions to the ActionLists collection. + /// + public override DesignerActionListCollection ActionLists + { + get + { + DesignerActionListCollection actionLists = new DesignerActionListCollection(); + actionLists.AddRange(base.ActionLists); + ContextMenuStripActionList cmActionList = new ContextMenuStripActionList(this); + if (cmActionList != null) + { + actionLists.Add(cmActionList); + } + // finally add the verbs for this component there... + DesignerVerbCollection cmVerbs = this.Verbs; + if (cmVerbs != null && cmVerbs.Count != 0) + { + DesignerVerb[] cmverbsArray = new DesignerVerb[cmVerbs.Count]; + cmVerbs.CopyTo(cmverbsArray, 0); + actionLists.Add(new DesignerActionVerbList(cmverbsArray)); + } + return actionLists; + } + } + + /// + /// The ToolStripItems are the associated components. We want those to come with in any cut, copy opreations. + /// + public override System.Collections.ICollection AssociatedComponents + { + get => ((ToolStrip)Component).Items; + } + + // Dummy menuItem that is used for the contextMenuStrip design + public ToolStripMenuItem DesignerMenuItem + { + get => menuItem; + } + + /// + /// Set by the ToolStripItemCollectionEditor when it's launched for this The Items property doesnt open another instance + /// of collectioneditor. We count this so that we can deal with nestings. + /// + internal bool EditingCollection + { + get => _editingCollection != 0; + set + { + if (value) + { + _editingCollection++; + } + else + { + _editingCollection--; + } + } + } + + // ContextMenuStrip if Inherited ACT as Readonly. + protected override InheritanceAttribute InheritanceAttribute + { + get + { + if ((base.InheritanceAttribute == InheritanceAttribute.Inherited)) + { + return InheritanceAttribute.InheritedReadOnly; + } + return base.InheritanceAttribute; + } + } + + /// + /// Prefilter this property so that we can set the right To Left on the Design Menu... + /// + private RightToLeft RightToLeft + { + get => dropDown.RightToLeft; + set + { + if (menuItem != null && designMenu != null && value != RightToLeft) + { + Rectangle bounds = Rectangle.Empty; + try + { + bounds = dropDown.Bounds; + menuItem.HideDropDown(); + designMenu.RightToLeft = value; + dropDown.RightToLeft = value; + } + finally + { + BehaviorService behaviorService = (BehaviorService)GetService(typeof(BehaviorService)); + if (behaviorService != null && bounds != Rectangle.Empty) + { + behaviorService.Invalidate(bounds); + } + ToolStripMenuItemDesigner itemDesigner = (ToolStripMenuItemDesigner)host.GetDesigner(menuItem); + if (itemDesigner != null) + { + itemDesigner.InitializeDropDown(); + } + } + } + } + } + + /// + /// shadowing the SettingsKey so we can default it to be RootComponent.Name + "." + Control.Name + /// + private string SettingsKey + { + get + { + if (string.IsNullOrEmpty((string)ShadowProperties["SettingsKey"])) + { + IPersistComponentSettings persistableComponent = Component as IPersistComponentSettings; + if (persistableComponent != null && host != null) + { + if (persistableComponent.SettingsKey == null) + { + IComponent rootComponent = host.RootComponent; + if (rootComponent != null && rootComponent != persistableComponent) + { + ShadowProperties["SettingsKey"] = string.Format(CultureInfo.CurrentCulture, "{0}.{1}", rootComponent.Site.Name, Component.Site.Name); + } + else + { + ShadowProperties["SettingsKey"] = Component.Site.Name; + } + } + persistableComponent.SettingsKey = ShadowProperties["SettingsKey"] as string; + return persistableComponent.SettingsKey; + } + } + return ShadowProperties["SettingsKey"] as string; + } + set + { + ShadowProperties["SettingsKey"] = value; + if (Component is IPersistComponentSettings persistableComponent) + { + persistableComponent.SettingsKey = value; + } + } + } + + // We have to add the glyphs ourselves. + private void AddSelectionGlyphs(SelectionManager selMgr, ISelectionService selectionService) + { + //If one or many of our items are selected then Add Selection Glyphs ourselces since this is a ComponentDesigner which wont get called on the "GetGlyphs" + ICollection selComponents = selectionService.GetSelectedComponents(); + GlyphCollection glyphs = new GlyphCollection(); + foreach (object selComp in selComponents) + { + if (selComp is ToolStripItem item) + { + ToolStripItemDesigner itemDesigner = (ToolStripItemDesigner)host.GetDesigner(item); + if (itemDesigner != null) + { + itemDesigner.GetGlyphs(ref glyphs, new ResizeBehavior(item.Site)); + } + } + } + // Get the Glyphs union Rectangle. + if (glyphs.Count > 0) + { + // Add Glyphs and then invalidate the unionRect + selMgr.SelectionGlyphAdorner.Glyphs.AddRange(glyphs); + } + } + + // internal method called by outside designers to add glyphs for the ContextMenuStrip + internal void AddSelectionGlyphs() + { + SelectionManager selMgr = (SelectionManager)GetService(typeof(SelectionManager)); + if (selMgr != null) + { + AddSelectionGlyphs(selMgr, selSvc); + } + } + + /// + /// Disposes of this designer. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + // Unhook our services + if (selSvc != null) + { + selSvc.SelectionChanged -= new EventHandler(this.OnSelectionChanged); + selSvc.SelectionChanging -= new EventHandler(this.OnSelectionChanging); + } + + DisposeMenu(); + if (designMenu != null) + { + designMenu.Dispose(); + designMenu = null; + } + if (dummyToolStripGlyph != null) + { + dummyToolStripGlyph = null; + } + if (undoEngine != null) + { + undoEngine.Undone -= new EventHandler(this.OnUndone); + } + } + base.Dispose(disposing); + } + + /// + /// Disposes of this dummy menuItem and its designer.. + /// + private void DisposeMenu() + { + HideMenu(); + if (host.RootComponent is Control form) + { + if (designMenu != null) + { + form.Controls.Remove(designMenu); + } + if (menuItem != null) + { + if (nestedContainer != null) + { + nestedContainer.Dispose(); + nestedContainer = null; + } + menuItem.Dispose(); + menuItem = null; + } + } + } + + // private helper function to Hide the ContextMenu structure. + private void HideMenu() + { + if (menuItem == null) + { + return; + } + //Add MenuBack + if (parentMenu != null && parentFormDesigner != null) + { + parentFormDesigner.Menu = parentMenu; + } + + selected = false; + if (host.RootComponent is Control form) + { + menuItem.DropDown.AutoClose = true; + menuItem.HideDropDown(); + menuItem.Visible = false; + //Hide the MenuItem DropDown. + designMenu.Visible = false; + //Invalidate the Bounds.. + ToolStripAdornerWindowService toolStripAdornerWindowService = (ToolStripAdornerWindowService)GetService(typeof(ToolStripAdornerWindowService)); + if (toolStripAdornerWindowService != null) + { + //toolStripAdornerWindowService.Invalidate(boundsToInvalidate); + toolStripAdornerWindowService.Invalidate(); + } + + //Query for the Behavior Service and Remove Glyph.... + BehaviorService behaviorService = (BehaviorService)GetService(typeof(BehaviorService)); + if (behaviorService != null) + { + if (dummyToolStripGlyph != null) + { + SelectionManager selMgr = (SelectionManager)GetService(typeof(SelectionManager)); + if (selMgr != null) + { + if (selMgr.BodyGlyphAdorner.Glyphs.Contains(dummyToolStripGlyph)) + { + selMgr.BodyGlyphAdorner.Glyphs.Remove(dummyToolStripGlyph); + } + selMgr.Refresh(); + } + } + dummyToolStripGlyph = null; + } + + //Unhook all the events for DesignMenuItem + if (menuItem != null) + { + if (host.GetDesigner(menuItem) is ToolStripMenuItemDesigner itemDesigner) + { + itemDesigner.UnHookEvents(); + itemDesigner.RemoveTypeHereNode(menuItem); + } + } + } + } + + /// + /// Initialize the item. + /// + // EditorServiceContext is newed up to add Edit Items verb. + [SuppressMessage("Microsoft.Performance", "CA1806:DoNotIgnoreMethodResults")] + public override void Initialize(IComponent component) + { + base.Initialize(component); + host = (IDesignerHost)GetService(typeof(IDesignerHost)); + //Add the EditService so that the ToolStrip can do its own Tab and Keyboard Handling + ToolStripKeyboardHandlingService keyboardHandlingService = (ToolStripKeyboardHandlingService)GetService(typeof(ToolStripKeyboardHandlingService)); + if (keyboardHandlingService == null) + { + keyboardHandlingService = new ToolStripKeyboardHandlingService(component.Site); + } + + //Add the InsituEditService so that the ToolStrip can do its own Insitu Editing + ISupportInSituService inSituService = (ISupportInSituService)GetService(typeof(ISupportInSituService)); + if (inSituService == null) + { + inSituService = new ToolStripInSituService(Component.Site); + } + + dropDown = (ToolStripDropDown)Component; + dropDown.Visible = false; + //shadow properties as we would change these for DropDowns at DesignTime. + AutoClose = dropDown.AutoClose; + AllowDrop = dropDown.AllowDrop; + + selSvc = (ISelectionService)GetService(typeof(ISelectionService)); + if (selSvc != null) + { + // first select the rootComponent and then hook on the events... but not if we are loading - VSWhidbey #484576 + if (host != null && !host.Loading) + { + selSvc.SetSelectedComponents(new IComponent[] { host.RootComponent }, SelectionTypes.Replace); + } + selSvc.SelectionChanging += new EventHandler(OnSelectionChanging); + selSvc.SelectionChanged += new EventHandler(OnSelectionChanged); + } + + designMenu = new MenuStrip + { + Visible = false, + AutoSize = false, + Dock = DockStyle.Top + }; + if (DpiHelper.IsScalingRequired) + { + designMenu.Height = DpiHelper.LogicalToDeviceUnitsY(designMenu.Height); + } + //Add MenuItem + if (host.RootComponent is Control form) + { + menuItem = new ToolStripMenuItem + { + BackColor = SystemColors.Window, + Name = Component.Site.Name + }; + menuItem.Text = (dropDown != null) ? dropDown.GetType().Name : menuItem.Name; + designMenu.Items.Add(menuItem); + form.Controls.Add(designMenu); + designMenu.SendToBack(); + nestedContainer = GetService(typeof(INestedContainer)) as INestedContainer; + if (nestedContainer != null) + { + nestedContainer.Add(menuItem, "ContextMenuStrip"); + } + } + + // init the verb. + new EditorServiceContext(this, TypeDescriptor.GetProperties(Component)["Items"], SR.ToolStripItemCollectionEditorVerb); + // use the UndoEngine.Undone to Show the DropDown Again.. + if (undoEngine == null) + { + undoEngine = GetService(typeof(UndoEngine)) as UndoEngine; + if (undoEngine != null) + { + undoEngine.Undone += new EventHandler(OnUndone); + } + } + } + + // Helper function to check if the ToolStripItem on the ContextMenu is selected. + private bool IsContextMenuStripItemSelected(ISelectionService selectionService) + { + bool showDesignMenu = false; + if (menuItem == null) + { + return showDesignMenu; + } + + ToolStripDropDown topmost = null; + IComponent comp = (IComponent)selectionService.PrimarySelection; + if (comp == null && dropDown.Visible) + { + ToolStripKeyboardHandlingService keyboardHandlingService = (ToolStripKeyboardHandlingService)GetService(typeof(ToolStripKeyboardHandlingService)); + if (keyboardHandlingService != null) + { + comp = (IComponent)keyboardHandlingService.SelectedDesignerControl; + } + } + // This case covers (a) and (b) above.... + if (comp is ToolStripDropDownItem) + { + if (comp is ToolStripDropDownItem currentItem && currentItem == menuItem) + { + topmost = menuItem.DropDown; + } + else + { + ToolStripMenuItemDesigner itemDesigner = (ToolStripMenuItemDesigner)host.GetDesigner(comp); + if (itemDesigner != null) + { + topmost = itemDesigner.GetFirstDropDown((ToolStripDropDownItem)comp); + } + } + } + else if (comp is ToolStripItem) //case (c) + { + if (!(((ToolStripItem)comp).GetCurrentParent() is ToolStripDropDown parent)) + { + // Try if the item has not laid out... + parent = ((ToolStripItem)comp).Owner as ToolStripDropDown; + } + if (parent != null && parent.Visible) + { + ToolStripItem ownerItem = parent.OwnerItem; + if (ownerItem != null && ownerItem == menuItem) + { + topmost = menuItem.DropDown; + } + else + { + ToolStripMenuItemDesigner itemDesigner = (ToolStripMenuItemDesigner)host.GetDesigner(ownerItem); + if (itemDesigner != null) + { + topmost = itemDesigner.GetFirstDropDown((ToolStripDropDownItem)ownerItem); + } + } + } + } + if (topmost != null) + { + ToolStripItem topMostItem = topmost.OwnerItem; + if (topMostItem == menuItem) + { + showDesignMenu = true; + } + } + return showDesignMenu; + } + + /// + /// Listens SelectionChanging to Show the MenuDesigner. + /// + private void OnSelectionChanging(object sender, EventArgs e) + { + ISelectionService selectionService = (ISelectionService)sender; + // If we are no longer selected ... Hide the DropDown + bool showDesignMenu = IsContextMenuStripItemSelected(selectionService) || Component.Equals(selectionService.PrimarySelection); + if (selected && !showDesignMenu) + { + HideMenu(); + } + } + + /// + /// Listens SelectionChanged to Show the MenuDesigner. + /// + private void OnSelectionChanged(object sender, EventArgs e) + { + if (Component == null || menuItem == null) + { + return; + } + ISelectionService selectionService = (ISelectionService)sender; + // Select the container if TopLevel Dummy MenuItem is selected. + if (selectionService.GetComponentSelected(menuItem)) + { + selectionService.SetSelectedComponents(new IComponent[] { Component }, SelectionTypes.Replace); + } + + //return if DropDown is already is selected. + if (Component.Equals(selectionService.PrimarySelection) && selected) + { + return; + } + + bool showDesignMenu = IsContextMenuStripItemSelected(selectionService) || Component.Equals(selectionService.PrimarySelection); + + if (showDesignMenu) + { + if (!dropDown.Visible) + { + ShowMenu(); + } + //Selection change would remove our Glyph from the BodyGlyph Collection. + SelectionManager selMgr = (SelectionManager)GetService(typeof(SelectionManager)); + if (selMgr != null) + { + if (dummyToolStripGlyph != null) + { + selMgr.BodyGlyphAdorner.Glyphs.Insert(0, dummyToolStripGlyph); + } + // Add our SelectionGlyphs and Invalidate. + AddSelectionGlyphs(selMgr, selectionService); + } + } + } + + + /// + /// Allows a designer to filter the set of properties the component it is designing will expose through the TypeDescriptor object. This method is called immediately before its corresponding "Post" method. If you are overriding this method you should call the base implementation before you perform your own filtering. + /// + protected override void PreFilterProperties(IDictionary properties) + { + base.PreFilterProperties(properties); + PropertyDescriptor prop; + string[] shadowProps = new string[] { "AutoClose", "SettingsKey", "RightToLeft", "AllowDrop" }; + Attribute[] empty = new Attribute[0]; + for (int i = 0; i < shadowProps.Length; i++) + { + prop = (PropertyDescriptor)properties[shadowProps[i]]; + if (prop != null) + { + properties[shadowProps[i]] = TypeDescriptor.CreateProperty(typeof(ToolStripDropDownDesigner), prop, empty); + } + } + } + + // Reset Settings. + public void ResetSettingsKey() + { + if (Component is IPersistComponentSettings persistableComponent) + { + SettingsKey = null; + } + } + + // + // Resets the ToolStripDropDown AutoClose to be the default padding + // + private void ResetAutoClose() + { + ShadowProperties["AutoClose"] = true; + } + + // + // Restores the ToolStripDropDown AutoClose to be the value set in the property grid. + // + private void RestoreAutoClose() + { + dropDown.AutoClose = (bool)ShadowProperties["AutoClose"]; + } + + // + // Resets the ToolStripDropDown AllowDrop to be the default padding + // + private void ResetAllowDrop() + { + ShadowProperties["AllowDrop"] = false; + } + + // + // Restores the ToolStripDropDown AllowDrop to be the value set in the property grid. + // + private void RestoreAllowDrop() + { + dropDown.AutoClose = (bool)ShadowProperties["AllowDrop"]; + } + + // + // Resets the ToolStripDropDown RightToLeft to be the default RightToLeft + // + private void ResetRightToLeft() + { + RightToLeft = RightToLeft.No; + } + + /// + /// Show the MenuDesigner; used by ToolStripmenuItemdesigner to show the menu when the user selects the dropDown item through the PG or Document outline. The editor node will be selected by default. + /// + public void ShowMenu() + { + int count = dropDown.Items.Count - 1; + if (count >= 0) + { + ShowMenu(dropDown.Items[count]); + } + else + { + ShowMenu(null); + } + } + + /// + /// Show the MenuDesigner; used by ToolStripmenuItemdesigner to show the menu when the user selects the dropDown item through the PG or Document outline. The input toolstrip item will be selected. + /// + public void ShowMenu(ToolStripItem selectedItem) + { + if (menuItem == null) + { + return; + } + Control parent = designMenu.Parent as Control; + if (parent is Form parentForm) + { + parentFormDesigner = host.GetDesigner(parentForm) as FormDocumentDesigner; + if (parentFormDesigner != null && parentFormDesigner.Menu != null) + { + parentMenu = parentFormDesigner.Menu; + parentFormDesigner.Menu = null; + } + } + selected = true; + designMenu.Visible = true; + designMenu.BringToFront(); + menuItem.Visible = true; + // Check if this is a design-time DropDown + if (currentParent != null && currentParent != menuItem) + { + if (host.GetDesigner(currentParent) is ToolStripMenuItemDesigner ownerItemDesigner) + { + ownerItemDesigner.RemoveTypeHereNode(currentParent); + } + } + + //Everytime you hide/show .. set the DropDown of the designer MenuItem to the component dropDown beign designed. + menuItem.DropDown = dropDown; + menuItem.DropDown.OwnerItem = menuItem; + if (dropDown.Items.Count > 0) + { + ToolStripItem[] items = new ToolStripItem[dropDown.Items.Count]; + dropDown.Items.CopyTo(items, 0); + foreach (ToolStripItem toolItem in items) + { + if (toolItem is DesignerToolStripControlHost) + { + dropDown.Items.Remove(toolItem); + } + } + } + + ToolStripMenuItemDesigner itemDesigner = (ToolStripMenuItemDesigner)host.GetDesigner(menuItem); + BehaviorService behaviorService = (BehaviorService)GetService(typeof(BehaviorService)); + if (behaviorService != null) + { + // Show the contextMenu only if the dummy menuStrip is contained in the Form. Refer to VsWhidbey 484317 for more details. + if (itemDesigner != null && parent != null) + { + Rectangle parentBounds = behaviorService.ControlRectInAdornerWindow(parent); + Rectangle menuBounds = behaviorService.ControlRectInAdornerWindow(designMenu); + if (ToolStripDesigner.IsGlyphTotallyVisible(menuBounds, parentBounds)) + { + itemDesigner.InitializeDropDown(); + } + } + + if (dummyToolStripGlyph == null) + { + Point loc = behaviorService.ControlToAdornerWindow(designMenu); + Rectangle r = designMenu.Bounds; + r.Offset(loc); + dummyToolStripGlyph = new ControlBodyGlyph(r, Cursor.Current, menuItem, new ContextMenuStripBehavior(menuItem)); + SelectionManager selMgr = (SelectionManager)GetService(typeof(SelectionManager)); + if (selMgr != null) + { + selMgr.BodyGlyphAdorner.Glyphs.Insert(0, dummyToolStripGlyph); + } + } + + if (selectedItem != null) + { + ToolStripKeyboardHandlingService keyboardHandlingService = (ToolStripKeyboardHandlingService)GetService(typeof(ToolStripKeyboardHandlingService)); + if (keyboardHandlingService != null) + { + keyboardHandlingService.SelectedDesignerControl = selectedItem; + } + } + } + } + + // Should the designer serialize the settings? + private bool ShouldSerializeSettingsKey() => (Component is IPersistComponentSettings persistableComponent && persistableComponent.SaveSettings && SettingsKey != null); + + // + // Since we're shadowing ToolStripDropDown AutoClose, we get called here to determine whether or not to serialize + // + private bool ShouldSerializeAutoClose() => (!(bool)ShadowProperties["AutoClose"]); + + // + // Since we're shadowing ToolStripDropDown AllowDrop, we get called here to determine whether or not to serialize + // + private bool ShouldSerializeAllowDrop() => AllowDrop; + + // + // Since we're shadowing ToolStripDropDown RightToLeft, we get called here to determine whether or not to serialize + // + private bool ShouldSerializeRightToLeft() => RightToLeft != RightToLeft.No; + + /// + /// ResumeLayout after Undone. + /// + private void OnUndone(object source, EventArgs e) + { + if (selSvc != null && Component.Equals(selSvc.PrimarySelection)) + { + HideMenu(); + ShowMenu(); + } + } + + /// + /// This is an internal class which provides the Behavior for our MenuStrip Body Glyph. This will just eat the MouseUps... + /// + internal class ContextMenuStripBehavior : System.Windows.Forms.Design.Behavior.Behavior + { + readonly ToolStripMenuItem _item; + internal ContextMenuStripBehavior(ToolStripMenuItem menuItem) + { + _item = menuItem; + } + + public override bool OnMouseUp(Glyph g, MouseButtons button) + { + if (button == MouseButtons.Left) + { + return true; + } + return false; + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDropDownItemDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDropDownItemDesigner.cs new file mode 100644 index 00000000000..000a4254095 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDropDownItemDesigner.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; + +namespace System.Windows.Forms.Design +{ + /// + /// Designer for ToolStripDropDownItems. This is here so only the dropdown items get the "Edit Items..." verb. + /// + internal class ToolStripDropDownItemDesigner : ToolStripItemDesigner + { + /// + /// Initialize the item. + /// + public override void Initialize(IComponent component) + { + base.Initialize(component); + } + + /// + /// The ToolStripItems are the associated components. We want those to come with in any cut, copy opreations. + /// + public override System.Collections.ICollection AssociatedComponents + { + get + { + if (Component is ToolStripDropDownItem item && item.DropDown.IsAutoGenerated) + { + return item.DropDownItems; + } + return base.AssociatedComponents; + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripEditorManager.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripEditorManager.cs new file mode 100644 index 00000000000..649a3961b2f --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripEditorManager.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// This internal Class is used by all TOPLEVEL ToolStripItems to show the InSitu Editor. When the ToolStripItem receives the MouseDown on its Glyph it calls the "ActivateEditor" Function on this EditorManager. The ActivateEditor Function checks for any existing "EDITOR" active, closes that down and now opens the new editor on the "AdornerWindow". This class is also responsible for "hookingup" to the F2 Command on VS. + /// + internal class ToolStripEditorManager + { + // Local copy of BehaviorService so that we can add the Insitu Editor to the AdornerWindow. + private readonly BehaviorService _behaviorService; + // Component for this InSitu Editor... (this is a ToolStripItem) that wants to go into InSitu + private readonly IDesignerHost _designerHost; + // This is always ToolStrip + private readonly IComponent _comp; + // The current Bounds of the Insitu Editor on Adorner Window.. These are required for invalidation. + private Rectangle _lastKnownEditorBounds = Rectangle.Empty; + // The encapsulated Editor. + private ToolStripEditorControl _editor; + // Actual ToolStripEditor for the current ToolStripItem. + private ToolStripTemplateNode _editorUI; + // The Current ToolStripItem that needs to go into the InSitu Mode. We keep a local copy so that when a new item comes in, we can "ROLLBACK" the existing edit. + private ToolStripItem _currentItem; + // The designer for current ToolStripItem. + private ToolStripItemDesigner _itemDesigner; + + public ToolStripEditorManager(IComponent comp) + { + // get the parent + _comp = comp; + _behaviorService = (BehaviorService)comp.Site.GetService(typeof(BehaviorService)); + _designerHost = (IDesignerHost)comp.Site.GetService(typeof(IDesignerHost)); + } + + /// + /// Activates the editor for the given item.If there's still an editor around for the previous-edited item, it is deactivated. Pass in 'null' to deactivate and remove the current editor, if any. + /// + internal void ActivateEditor(ToolStripItem item, bool clicked) + { + if (item != _currentItem) + { + // Remove old editor + if (_editor != null) + { + _behaviorService.AdornerWindowControl.Controls.Remove(_editor); + _behaviorService.Invalidate(_editor.Bounds); + _editorUI = null; + _editor = null; + _currentItem = null; + _itemDesigner.IsEditorActive = false; + + // Show the previously edited glyph + if (_currentItem != null) + { + _currentItem = null; + } + } + if (item != null) + { + // Add new editor from the item... + _currentItem = item; + if (_designerHost != null) + { + _itemDesigner = (ToolStripItemDesigner)_designerHost.GetDesigner(_currentItem); + } + _editorUI = (ToolStripTemplateNode)_itemDesigner.Editor; + // If we got an editor, position and focus it. + if (_editorUI != null) + { + // Hide this glyph while it's being edited + _itemDesigner.IsEditorActive = true; + _editor = new ToolStripEditorControl(_editorUI.EditorToolStrip, _editorUI.Bounds); + _behaviorService.AdornerWindowControl.Controls.Add(_editor); + _lastKnownEditorBounds = _editor.Bounds; + _editor.BringToFront(); + // this is important since the ToolStripEditorControl listens to textchanged messages from TextBox. + _editorUI.ignoreFirstKeyUp = true; + // Select the Editor... Put Text and Select it ... + _editorUI.FocusEditor(_currentItem); + } + } + } + } + + /// + /// This will remove the Command for F2. + /// + internal void CloseManager() + { + } + + /// + /// This LISTENs to the Editor Resize for resizing the Insitu edit on the Adorner Window ... CURRENTLY DISABLED. + /// + private void OnEditorResize(object sender, EventArgs e) + { + // THIS IS CURRENTLY DISABLE !!!!! TO DO !! SHOULD WE SUPPORT AUTOSIZED INSITU ????? + _behaviorService.Invalidate(_lastKnownEditorBounds); + if (_editor != null) + { + _lastKnownEditorBounds = _editor.Bounds; + } + } + + // Private Class Implemented for InSitu Editor. This class just Wraps the ToolStripEditor from the TemplateNode and puts it in a Panel. + private class ToolStripEditorControl : Panel + { + private readonly Control _wrappedEditor; + private Rectangle _bounds; + + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + public ToolStripEditorControl(Control editorToolStrip, Rectangle bounds) + { + _wrappedEditor = editorToolStrip; + Bounds1 = bounds; + _wrappedEditor.Resize += new EventHandler(OnWrappedEditorResize); + Controls.Add(editorToolStrip); + Location = new Point(bounds.X, bounds.Y); + Text = "InSituEditorWrapper"; + UpdateSize(); + } + + public Rectangle Bounds1 { get => _bounds; set => _bounds = value; } + + private void OnWrappedEditorResize(object sender, EventArgs e) + { + } + + private void UpdateSize() => Size = new Size(_wrappedEditor.Size.Width, _wrappedEditor.Size.Height); + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripInSituService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripInSituService.cs new file mode 100644 index 00000000000..95e8a37026a --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripInSituService.cs @@ -0,0 +1,251 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; + +namespace System.Windows.Forms.Design +{ + /// + /// This class implements the ISupportInSituService which enables some designers to go into InSitu Editing when Keys are pressed while the Component is Selected. + /// + internal class ToolStripInSituService : ISupportInSituService, IDisposable + { + private readonly IServiceProvider _sp; + private readonly IDesignerHost _designerHost; + private IComponentChangeService _componentChangeSvc; + private ToolStripDesigner _toolDesigner; + private ToolStripItemDesigner _toolItemDesigner; + private ToolStripKeyboardHandlingService _toolStripKeyBoardService; + + /// + /// The constructor for this class which takes the serviceprovider used to get the selectionservice. This ToolStripInSituService is ToolStrip specific. + /// + public ToolStripInSituService(IServiceProvider provider) + { + _sp = provider; + _designerHost = (IDesignerHost)provider.GetService(typeof(IDesignerHost)); + Debug.Assert(_designerHost != null, "ToolStripKeyboardHandlingService relies on the selection service, which is unavailable."); + if (_designerHost != null) + { + _designerHost.AddService(typeof(ISupportInSituService), this); + } + _componentChangeSvc = (IComponentChangeService)_designerHost.GetService(typeof(IComponentChangeService)); + Debug.Assert(_componentChangeSvc != null, "ToolStripKeyboardHandlingService relies on the componentChange service, which is unavailable."); + if (_componentChangeSvc != null) + { + _componentChangeSvc.ComponentRemoved += new ComponentEventHandler(OnComponentRemoved); + } + } + + /// + /// Disposes of this object, removing all commands from the menu service. + /// + public void Dispose() + { + if (_toolDesigner != null) + { + _toolDesigner.Dispose(); + _toolDesigner = null; + } + if (_toolItemDesigner != null) + { + _toolItemDesigner.Dispose(); + _toolItemDesigner = null; + } + if (_componentChangeSvc != null) + { + _componentChangeSvc.ComponentRemoved -= new ComponentEventHandler(OnComponentRemoved); + _componentChangeSvc = null; + } + } + + private ToolStripKeyboardHandlingService ToolStripKeyBoardService + { + get + { + if (_toolStripKeyBoardService == null) + { + _toolStripKeyBoardService = (ToolStripKeyboardHandlingService)_sp.GetService(typeof(ToolStripKeyboardHandlingService)); + } + return _toolStripKeyBoardService; + } + } + + /// + /// Returning true for IgnoreMessages means that this service is interested in getting the KeyBoard characters. + /// + public bool IgnoreMessages + { + get + { + ISelectionService selectionService = (ISelectionService)_sp.GetService(typeof(ISelectionService)); + IDesignerHost host = (IDesignerHost)_sp.GetService(typeof(IDesignerHost)); + if (selectionService != null && host != null) + { + if (!(selectionService.PrimarySelection is IComponent comp)) + { + comp = (IComponent)ToolStripKeyBoardService.SelectedDesignerControl; + } + if (comp != null) + { + if (comp is DesignerToolStripControlHost c) + { + if (c.GetCurrentParent() is ToolStripDropDown dropDown) + { + if (dropDown.OwnerItem is ToolStripDropDownItem parentItem) + { + if (parentItem is ToolStripOverflowButton) + { + return false; + } + else + { + _toolItemDesigner = host.GetDesigner(parentItem) as ToolStripMenuItemDesigner; + if (_toolItemDesigner != null) + { + _toolDesigner = null; + return true; + } + } + } + } + else + { + if (c.GetCurrentParent() is MenuStrip tool) + { + _toolDesigner = host.GetDesigner(tool) as ToolStripDesigner; + if (_toolDesigner != null) + { + _toolItemDesigner = null; + return true; + } + } + } + } + else if (comp is ToolStripDropDown) //case for ToolStripDropDown.. + { + if (host.GetDesigner(comp) is ToolStripDropDownDesigner designer) + { + ToolStripMenuItem toolItem = designer.DesignerMenuItem; + if (toolItem != null) + { + _toolItemDesigner = host.GetDesigner(toolItem) as ToolStripItemDesigner; + if (_toolItemDesigner != null) + { + _toolDesigner = null; + return true; + } + } + } + } + else if (comp is MenuStrip) + { + _toolDesigner = host.GetDesigner(comp) as ToolStripDesigner; + if (_toolDesigner != null) + { + _toolItemDesigner = null; + return true; + } + } + else if (comp is ToolStripMenuItem) + { + _toolItemDesigner = host.GetDesigner(comp) as ToolStripItemDesigner; + if (_toolItemDesigner != null) + { + _toolDesigner = null; + return true; + } + } + } + } + return false; + } + } + + /// + /// This function is called on the service when the PBRSFORWARD gets the first WM_CHAR message. + /// + public void HandleKeyChar() + { + if (_toolDesigner != null || _toolItemDesigner != null) + { + if (_toolDesigner != null) + { + _toolDesigner.ShowEditNode(false); + } + else if (_toolItemDesigner != null) + { + if (_toolItemDesigner is ToolStripMenuItemDesigner menuDesigner) + { + ISelectionService selService = (ISelectionService)_sp.GetService(typeof(ISelectionService)); + if (selService != null) + { + object comp = selService.PrimarySelection; + if (comp == null) + { + comp = ToolStripKeyBoardService.SelectedDesignerControl; + } + DesignerToolStripControlHost designerItem = comp as DesignerToolStripControlHost; + if (designerItem != null || comp is ToolStripDropDown) + { + menuDesigner.EditTemplateNode(false); + } + else + { + menuDesigner.ShowEditNode(false); + } + } + } + else + { + _toolItemDesigner.ShowEditNode(false); + } + } + } + } + + /// + /// This function returns the Window handle that should get all the Keyboard messages. + /// + public IntPtr GetEditWindow() + { + IntPtr hWnd = IntPtr.Zero; + if (_toolDesigner != null && _toolDesigner.Editor != null && _toolDesigner.Editor.EditBox != null) + { + hWnd = (_toolDesigner.Editor.EditBox.Visible) ? _toolDesigner.Editor.EditBox.Handle : hWnd; + } + else if (_toolItemDesigner != null && _toolItemDesigner.Editor != null && _toolItemDesigner.Editor.EditBox != null) + { + hWnd = (_toolItemDesigner.Editor.EditBox.Visible) ? _toolItemDesigner.Editor.EditBox.Handle : hWnd; + } + return hWnd; + } + + // Remove the Service when the last toolStrip is removed. + private void OnComponentRemoved(object sender, ComponentEventArgs e) + { + bool toolStripPresent = false; + ComponentCollection comps = _designerHost.Container.Components; + foreach (IComponent comp in comps) + { + if (comp is ToolStrip) + { + toolStripPresent = true; + break; + } + } + if (!toolStripPresent) + { + ToolStripInSituService inSituService = (ToolStripInSituService)_sp.GetService(typeof(ISupportInSituService)); + if (inSituService != null) + { + //since we are going away .. restore the old commands. + _designerHost.RemoveService(typeof(ISupportInSituService)); + } + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemBehavior.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemBehavior.cs new file mode 100644 index 00000000000..2c0f1251d00 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemBehavior.cs @@ -0,0 +1,985 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// The behavior for the glpyh that covers the items themselves. This selects the items when they are clicked, and will (when implemented) do the dragging/reordering of them. + /// + internal class ToolStripItemBehavior : Behavior.Behavior + { + private const int GLYPHBORDER = 1; + private const int GLYPHINSET = 2; + //new privates for "Drag Drop" + internal Rectangle _dragBoxFromMouseDown = Rectangle.Empty; + private Timer _timer = null; + private ToolStripItemGlyph _selectedGlyph; + private bool _doubleClickFired = false; + private bool _mouseUpFired = false; + private Control _dropSource; + private IEventHandlerService _eventSvc = null; + + public ToolStripItemBehavior() + { + } + + // Gets the DropSource control. + private Control DropSource + { + get + { + if (_dropSource == null) + { + _dropSource = new Control(); + } + return _dropSource; + } + } + + + // Returns true if oldSelection and newSelection have a commonParent. + private bool CommonParent(ToolStripItem oldSelection, ToolStripItem newSelection) + { + ToolStrip oldSelectionParent = oldSelection.GetCurrentParent(); + ToolStrip newSelectionParent = newSelection.GetCurrentParent(); + return (oldSelectionParent == newSelectionParent); + } + + /// + /// Clears the insertion mark when items are being reordered + /// + private void ClearInsertionMark(ToolStripItem item) + { + // Dont paint if cursor hasnt moved. + if (ToolStripDesigner.s_lastCursorPosition != Point.Empty && ToolStripDesigner.s_lastCursorPosition == Cursor.Position) + { + return; + } + // Dont paint any "MouseOver" glyohs if TemplateNode is ACTIVE ! + ToolStripKeyboardHandlingService keyService = GetKeyBoardHandlingService(item); + if (keyService != null && keyService.TemplateNodeActive) + { + return; + } + + // stuff away the lastInsertionMarkRect and clear it out _before_ we call paint OW the call to invalidate wont help as it will get repainted. + if (item != null && item.Site != null) + { + IDesignerHost designerHost = (IDesignerHost)item.Site.GetService(typeof(IDesignerHost)); + if (designerHost != null) + { + Rectangle bounds = GetPaintingBounds(designerHost, item); + bounds.Inflate(GLYPHBORDER, GLYPHBORDER); + Region rgn = new Region(bounds); + try + { + bounds.Inflate(-GLYPHINSET, -GLYPHINSET); + rgn.Exclude(bounds); + BehaviorService bSvc = GetBehaviorService(item); + if (bSvc != null && bounds != Rectangle.Empty) + { + bSvc.Invalidate(rgn); + } + } + finally + { + rgn.Dispose(); + rgn = null; + } + } + } + } + + // Tries to put the item in the Insitu edit mode after the double click timer has ticked + private void EnterInSituMode(ToolStripItemGlyph glyph) + { + if (glyph.ItemDesigner != null && !glyph.ItemDesigner.IsEditorActive) + { + glyph.ItemDesigner.ShowEditNode(false); + } + } + + // Gets the Selection Service. + private ISelectionService GetSelectionService(ToolStripItem item) + { + Debug.Assert(item != null, "Item passed is null, SelectionService cannot be obtained"); + if (item.Site != null) + { + ISelectionService selSvc = (ISelectionService)item.Site.GetService(typeof(ISelectionService)); + Debug.Assert(selSvc != null, "Failed to get Selection Service!"); + return selSvc; + } + return null; + } + + // Gets the Behavior Service. + private BehaviorService GetBehaviorService(ToolStripItem item) + { + Debug.Assert(item != null, "Item passed is null, BehaviorService cannot be obtained"); + if (item.Site != null) + { + BehaviorService behaviorSvc = (BehaviorService)item.Site.GetService(typeof(BehaviorService)); + Debug.Assert(behaviorSvc != null, "Failed to get Behavior Service!"); + return behaviorSvc; + } + return null; + } + + // Gets the ToolStripKeyBoardHandling Service. + private ToolStripKeyboardHandlingService GetKeyBoardHandlingService(ToolStripItem item) + { + Debug.Assert(item != null, "Item passed is null, ToolStripKeyBoardHandlingService cannot be obtained"); + if (item.Site != null) + { + ToolStripKeyboardHandlingService keyBoardSvc = (ToolStripKeyboardHandlingService)item.Site.GetService(typeof(ToolStripKeyboardHandlingService)); + Debug.Assert(keyBoardSvc != null, "Failed to get ToolStripKeyboardHandlingService!"); + return keyBoardSvc; + } + return null; + } + + // Gets the painting rect for SelectionRects + private static Rectangle GetPaintingBounds(IDesignerHost designerHost, ToolStripItem item) + { + Rectangle bounds = Rectangle.Empty; + if (designerHost.GetDesigner(item) is ToolStripItemDesigner itemDesigner) + { + bounds = itemDesigner.GetGlyphBounds(); + ToolStripDesignerUtils.GetAdjustedBounds(item, ref bounds); + // So that the mouseOver glyph matches the selectionGlyph. + bounds.Inflate(1, 1); + bounds.Width--; + bounds.Height--; + } + return bounds; + } + + // This helper function will return true if any other MouseHandler (say TabOrder UI) is active, in which case we should not handle any Mouse Messages.. Since the TabOrder UI is pre-Whidbey when the TabOrder UI is up, It adds a new Overlay (a window) to the DesignerFrame (something similar to AdornerWindow). This UI is a transaparent control which has overrides foir Mouse Messages. It listens for all mouse messages through the IMouseHandler interface instead of using the new BehaviorService. Hence we have to special case this scenario. (CONTROL DESIGNER ALSO DOES THIS). + private bool MouseHandlerPresent(ToolStripItem item) + { + IMouseHandler mouseHandler = null; + if (_eventSvc == null) + { + _eventSvc = (IEventHandlerService)item.Site.GetService(typeof(IEventHandlerService)); + } + if (_eventSvc != null) + { + mouseHandler = (IMouseHandler)_eventSvc.GetHandler(typeof(IMouseHandler)); + } + return (mouseHandler != null); + } + + // Occurs when the timer ticks after user has doubleclicked an item + private void OnDoubleClickTimerTick(object sender, EventArgs e) + { + if (_timer != null) + { + _timer.Enabled = false; + _timer.Tick -= new System.EventHandler(OnDoubleClickTimerTick); + _timer.Dispose(); + _timer = null; + // Enter Insitu ... + if (_selectedGlyph != null && _selectedGlyph.Item is ToolStripMenuItem) + { + EnterInSituMode(_selectedGlyph); + } + } + } + + + + + // Occurs when user doubleclicks on the TooLStripItem glyph + public override bool OnMouseDoubleClick(Glyph g, MouseButtons button, Point mouseLoc) + { + if (_mouseUpFired) + { + _doubleClickFired = true; + } + return false; + } + + // Occurs when MouseUp TooLStripItem glyph + public override bool OnMouseUp(Glyph g, MouseButtons button) + { + ToolStripItemGlyph glyph = g as ToolStripItemGlyph; + ToolStripItem glyphItem = glyph.Item; + if (MouseHandlerPresent(glyphItem)) + { + return false; + } + SetParentDesignerValuesForDragDrop(glyphItem, false, Point.Empty); + if (_doubleClickFired) + { + if (glyph != null && button == MouseButtons.Left) + { + ISelectionService selSvc = GetSelectionService(glyphItem); + if (selSvc == null) + { + return false; + } + + ToolStripItem selectedItem = selSvc.PrimarySelection as ToolStripItem; + // Check if this item is already selected ... + if (selectedItem == glyphItem) + { + // If timer != null.. we are in DoubleClick before the "InSitu Timer" so KILL IT. + if (_timer != null) + { + _timer.Enabled = false; + _timer.Tick -= new System.EventHandler(OnDoubleClickTimerTick); + _timer.Dispose(); + _timer = null; + } + // If the Selecteditem is already in editmode ... bail out + if (selectedItem != null) + { + ToolStripItemDesigner selectedItemDesigner = glyph.ItemDesigner; + if (selectedItemDesigner != null && selectedItemDesigner.IsEditorActive) + { + return false; + } + selectedItemDesigner.DoDefaultAction(); + } + _doubleClickFired = false; + _mouseUpFired = false; + } + } + } + else + { + _mouseUpFired = true; + } + return false; + } + + // Occurs when MouseDown on the TooLStripItem glyph + public override bool OnMouseDown(Glyph g, MouseButtons button, Point mouseLoc) + { + ToolStripItemGlyph glyph = g as ToolStripItemGlyph; + ToolStripItem glyphItem = glyph.Item; + ISelectionService selSvc = GetSelectionService(glyphItem); + BehaviorService bSvc = GetBehaviorService(glyphItem); + ToolStripKeyboardHandlingService keyService = GetKeyBoardHandlingService(glyphItem); + if ((button == MouseButtons.Left) && (keyService != null) && (keyService.TemplateNodeActive)) + { + if (keyService.ActiveTemplateNode.IsSystemContextMenuDisplayed) + { + // DevDiv Bugs: 144618 : skip behaviors when the context menu is displayed + return false; + } + } + + IDesignerHost designerHost = (IDesignerHost)glyphItem.Site.GetService(typeof(IDesignerHost)); + Debug.Assert(designerHost != null, "Invalid DesignerHost"); + + //Cache original selection + ICollection originalSelComps = null; + if (selSvc != null) + { + originalSelComps = selSvc.GetSelectedComponents(); + } + + // Add the TemplateNode to the Selection if it is currently Selected as the GetSelectedComponents wont do it for us. + ArrayList origSel = new ArrayList(originalSelComps); + if (origSel.Count == 0) + { + if (keyService != null && keyService.SelectedDesignerControl != null) + { + origSel.Add(keyService.SelectedDesignerControl); + } + } + + if (keyService != null) + { + keyService.SelectedDesignerControl = null; + if (keyService.TemplateNodeActive) + { + // If templateNode Active .. commit and Select it + keyService.ActiveTemplateNode.CommitAndSelect(); + // if the selected item is clicked .. then commit the node and reset the selection (refer 488002) + if (selSvc.PrimarySelection is ToolStripItem currentSel && currentSel == glyphItem) + { + selSvc.SetSelectedComponents(null, SelectionTypes.Replace); + } + } + } + + if (selSvc == null || MouseHandlerPresent(glyphItem)) + { + return false; + } + + if (glyph != null && button == MouseButtons.Left) + { + ToolStripItem selectedItem = selSvc.PrimarySelection as ToolStripItem; + // Always set the Drag-Rect for Drag-Drop... + SetParentDesignerValuesForDragDrop(glyphItem, true, mouseLoc); + // Check if this item is already selected ... + if (selectedItem != null && selectedItem == glyphItem) + { + // If the Selecteditem is already in editmode ... bail out + if (selectedItem != null) + { + ToolStripItemDesigner selectedItemDesigner = glyph.ItemDesigner; + if (selectedItemDesigner != null && selectedItemDesigner.IsEditorActive) + { + return false; + } + } + + // Check if this is CTRL + Click or SHIFT + Click, if so then just remove the selection + bool removeSel = (Control.ModifierKeys & (Keys.Control | Keys.Shift)) > 0; + if (removeSel) + { + selSvc.SetSelectedComponents(new IComponent[] { selectedItem }, SelectionTypes.Remove); + return false; + } + + //start Double Click Timer + // This is required for the second down in selection which can be the first down of a Double click on the glyph confusing... hence this comment ... + // Heres the scenario .... + // DOWN 1 - selects the ITEM + // DOWN 2 - ITEM goes into INSITU.... + // DOUBLE CLICK - dont show code.. + // Open INSITU after the double click time + if (selectedItem is ToolStripMenuItem) + { + _timer = new Timer + { + Interval = SystemInformation.DoubleClickTime + }; + _timer.Tick += new EventHandler(OnDoubleClickTimerTick); + _timer.Enabled = true; + _selectedGlyph = glyph; + } + } + else + { + bool shiftPressed = (Control.ModifierKeys & Keys.Shift) > 0; + // We should process MouseDown only if we are not yet selected.... + if (!selSvc.GetComponentSelected(glyphItem)) + { + //Reset the State... On the Glpyhs .. we get MouseDown - Mouse UP (for single Click) And we get MouseDown - MouseUp - DoubleClick - Up (for double Click) Hence reset the state at start.... + _mouseUpFired = false; + _doubleClickFired = false; + //Implementing Shift + Click.... + // we have 2 items, namely, selectedItem (current PrimarySelection) and glyphItem (item which has received mouseDown) FIRST check if they have common parent... IF YES then get the indices of the two and SELECT all items from LOWER index to the HIGHER index. + if (shiftPressed && (selectedItem != null && CommonParent(selectedItem, glyphItem))) + { + ToolStrip parent = null; + if (glyphItem.IsOnOverflow) + { + parent = glyphItem.Owner; + } + else + { + parent = glyphItem.GetCurrentParent(); + } + int startIndexOfSelection = Math.Min(parent.Items.IndexOf(selectedItem), parent.Items.IndexOf(glyphItem)); + int endIndexOfSelection = Math.Max(parent.Items.IndexOf(selectedItem), parent.Items.IndexOf(glyphItem)); + int countofItemsSelected = (endIndexOfSelection - startIndexOfSelection) + 1; + + // if two adjacent items are selected ... + if (countofItemsSelected == 2) + { + selSvc.SetSelectedComponents(new IComponent[] { glyphItem }); + } + else + { + object[] totalObjects = new object[countofItemsSelected]; + int j = 0; + for (int i = startIndexOfSelection; i <= endIndexOfSelection; i++) + { + totalObjects[j++] = parent.Items[i]; + } + selSvc.SetSelectedComponents(new IComponent[] { parent }, SelectionTypes.Replace); + ToolStripDesigner.s_shiftState = true; + selSvc.SetSelectedComponents(totalObjects, SelectionTypes.Replace); + } + } + //End Implmentation + else + { + if (glyphItem.IsOnDropDown && ToolStripDesigner.s_shiftState) + { + //Invalidate glyh only if we are in ShiftState... + ToolStripDesigner.s_shiftState = false; + if (bSvc != null) + { + bSvc.Invalidate(glyphItem.Owner.Bounds); + } + } + selSvc.SetSelectedComponents(new IComponent[] { glyphItem }, SelectionTypes.Auto); + } + // Set the appropriate object. + if (keyService != null) + { + keyService.ShiftPrimaryItem = glyphItem; + } + } + // we are already selected and if shiftpressed... + else if (shiftPressed || (Control.ModifierKeys & Keys.Control) > 0) + { + selSvc.SetSelectedComponents(new IComponent[] { glyphItem }, SelectionTypes.Remove); + } + } + } + + if (glyph != null && button == MouseButtons.Right) + { + if (!selSvc.GetComponentSelected(glyphItem)) + { + selSvc.SetSelectedComponents(new IComponent[] { glyphItem }); + } + } + + // finally Invalidate all selections + ToolStripDesignerUtils.InvalidateSelection(origSel, glyphItem, glyphItem.Site, false); + return false; + } + + /// + /// Overriden to paint the border on mouse enter..... + /// + public override bool OnMouseEnter(Glyph g) + { + if (g is ToolStripItemGlyph glyph) + { + ToolStripItem glyphItem = glyph.Item; + if (MouseHandlerPresent(glyphItem)) + { + return false; + } + + ISelectionService selSvc = GetSelectionService(glyphItem); + if (selSvc != null) + { + if (!selSvc.GetComponentSelected(glyphItem)) + { + PaintInsertionMark(glyphItem); + } + } + } + return false; + } + + /// + /// overriden to "clear" the boundary-paint when the mouse leave the item + /// + public override bool OnMouseLeave(Glyph g) + { + if (g is ToolStripItemGlyph glyph) + { + ToolStripItem glyphItem = glyph.Item; + if (MouseHandlerPresent(glyphItem)) + { + return false; + } + ISelectionService selSvc = GetSelectionService(glyphItem); + if (selSvc != null) + { + if (!selSvc.GetComponentSelected(glyphItem)) + { + ClearInsertionMark(glyphItem); + } + } + } + return false; + } + + /// + /// When any MouseMove message enters the BehaviorService's AdornerWindow (mousemove, ncmousemove) it is first passed here, to the top-most Behavior in the BehaviorStack. Returning 'true' from this function signifies that the Message was 'handled' by the Behavior and should not continue to be processed. + /// + public override bool OnMouseMove(Glyph g, MouseButtons button, Point mouseLoc) + { + bool retVal = false; + ToolStripItemGlyph glyph = g as ToolStripItemGlyph; + ToolStripItem glyphItem = glyph.Item; + ISelectionService selSvc = GetSelectionService(glyphItem); + if (selSvc == null || glyphItem.Site == null || MouseHandlerPresent(glyphItem)) + { + return false; + } + if (!selSvc.GetComponentSelected(glyphItem)) + { + PaintInsertionMark(glyphItem); + retVal = false; + } + + if (button == MouseButtons.Left && glyph != null && glyph.ItemDesigner != null && !glyph.ItemDesigner.IsEditorActive) + { + Rectangle dragBox = Rectangle.Empty; + IDesignerHost designerHost = (IDesignerHost)glyphItem.Site.GetService(typeof(IDesignerHost)); + Debug.Assert(designerHost != null, "Invalid DesignerHost"); + if (glyphItem.Placement == ToolStripItemPlacement.Overflow || (glyphItem.Placement == ToolStripItemPlacement.Main && !(glyphItem.IsOnDropDown))) + { + ToolStripItemDesigner itemDesigner = glyph.ItemDesigner; + ToolStrip parentToolStrip = itemDesigner.GetMainToolStrip(); + if (designerHost.GetDesigner(parentToolStrip) is ToolStripDesigner parentDesigner) + { + dragBox = parentDesigner.DragBoxFromMouseDown; + } + } + else if (glyphItem.IsOnDropDown) + { + //Get the OwnerItem's Designer and set the value... + if (glyphItem.Owner is ToolStripDropDown parentDropDown) + { + ToolStripItem ownerItem = parentDropDown.OwnerItem; + if (designerHost.GetDesigner(ownerItem) is ToolStripItemDesigner ownerItemDesigner) + { + dragBox = ownerItemDesigner.dragBoxFromMouseDown; + } + } + } + // If the mouse moves outside the rectangle, start the drag. + if (dragBox != Rectangle.Empty && !dragBox.Contains(mouseLoc.X, mouseLoc.Y)) + { + if (_timer != null) + { + _timer.Enabled = false; + _timer.Tick -= new System.EventHandler(OnDoubleClickTimerTick); + _timer.Dispose(); + _timer = null; + } + + // Proceed with the drag and drop, passing in the list item. + try + { + ArrayList dragItems = new ArrayList(); + ICollection selComps = selSvc.GetSelectedComponents(); + //create our list of controls-to-drag + foreach (IComponent comp in selComps) + { + if (comp is ToolStripItem item) + { + dragItems.Add(item); + } + } + + //Start Drag-Drop only if ToolStripItem is the primary Selection + if (selSvc.PrimarySelection is ToolStripItem selectedItem) + { + ToolStrip owner = selectedItem.Owner; + ToolStripItemDataObject data = new ToolStripItemDataObject(dragItems, selectedItem, owner); + DropSource.QueryContinueDrag += new QueryContinueDragEventHandler(QueryContinueDrag); + if (glyphItem is ToolStripDropDownItem ddItem) + { + if (designerHost.GetDesigner(ddItem) is ToolStripMenuItemDesigner itemDesigner) + { + itemDesigner.InitializeBodyGlyphsForItems(false, ddItem); + ddItem.HideDropDown(); + } + } + else if (glyphItem.IsOnDropDown && !glyphItem.IsOnOverflow) + { + ToolStripDropDown dropDown = glyphItem.GetCurrentParent() as ToolStripDropDown; + ToolStripDropDownItem ownerItem = dropDown.OwnerItem as ToolStripDropDownItem; + selSvc.SetSelectedComponents(new IComponent[] { ownerItem }, SelectionTypes.Replace); + } + DropSource.DoDragDrop(data, DragDropEffects.All); + } + } + finally + { + DropSource.QueryContinueDrag -= new QueryContinueDragEventHandler(QueryContinueDrag); + //Reset all Drag-Variables + SetParentDesignerValuesForDragDrop(glyphItem, false, Point.Empty); + ToolStripDesigner.s_dragItem = null; + _dropSource = null; + } + retVal = false; + } + } + return retVal; + } + + // OLE DragDrop virtual methods + /// + /// OnDragDrop can be overridden so that a Behavior can specify its own Drag/Drop rules. + /// + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + public override void OnDragDrop(Glyph g, DragEventArgs e) + { + ToolStripItem currentDropItem = ToolStripDesigner.s_dragItem; + // Ensure that the list item index is contained in the data. + if (e.Data is ToolStripItemDataObject && currentDropItem != null) + { + ToolStripItemDataObject data = (ToolStripItemDataObject)e.Data; + // Get the PrimarySelection before the Drag operation... + ToolStripItem selectedItem = data.PrimarySelection; + IDesignerHost designerHost = (IDesignerHost)currentDropItem.Site.GetService(typeof(IDesignerHost)); + Debug.Assert(designerHost != null, "Invalid DesignerHost"); + //Do DragDrop only if currentDropItem has changed. + if (currentDropItem != selectedItem && designerHost != null) + { + ArrayList components = data.DragComponents; + ToolStrip parentToolStrip = currentDropItem.GetCurrentParent() as ToolStrip; + int primaryIndex = -1; + string transDesc; + bool copy = (e.Effect == DragDropEffects.Copy); + if (components.Count == 1) + { + string name = TypeDescriptor.GetComponentName(components[0]); + if (name == null || name.Length == 0) + { + name = components[0].GetType().Name; + } + transDesc = string.Format(copy ? SR.BehaviorServiceCopyControl : SR.BehaviorServiceMoveControl, name); + } + else + { + transDesc = string.Format(copy ? SR.BehaviorServiceCopyControls : SR.BehaviorServiceMoveControls, components.Count); + } + + DesignerTransaction designerTransaction = designerHost.CreateTransaction(transDesc); + try + { + IComponentChangeService changeSvc = (IComponentChangeService)currentDropItem.Site.GetService(typeof(IComponentChangeService)); + if (changeSvc != null) + { + if (parentToolStrip is ToolStripDropDown dropDown) + { + ToolStripItem ownerItem = dropDown.OwnerItem; + changeSvc.OnComponentChanging(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"]); + } + else + { + changeSvc.OnComponentChanging(parentToolStrip, TypeDescriptor.GetProperties(parentToolStrip)["Items"]); + } + } + + // If we are copying, then we want to make a copy of the components we are dragging + if (copy) + { + // Remember the primary selection if we had one + if (selectedItem != null) + { + primaryIndex = components.IndexOf(selectedItem); + } + ToolStripKeyboardHandlingService keyboardHandlingService = GetKeyBoardHandlingService(selectedItem); + if (keyboardHandlingService != null) + { + keyboardHandlingService.CopyInProgress = true; + } + components = DesignerUtils.CopyDragObjects(components, currentDropItem.Site) as ArrayList; + if (keyboardHandlingService != null) + { + keyboardHandlingService.CopyInProgress = false; + } + if (primaryIndex != -1) + { + selectedItem = components[primaryIndex] as ToolStripItem; + } + } + + if (e.Effect == DragDropEffects.Move || copy) + { + ISelectionService selSvc = GetSelectionService(currentDropItem); + if (selSvc != null) + { + // Insert the item. + if (parentToolStrip is ToolStripOverflow) + { + parentToolStrip = (((ToolStripOverflow)parentToolStrip).OwnerItem).Owner; + } + + int indexOfItemUnderMouseToDrop = parentToolStrip.Items.IndexOf(ToolStripDesigner.s_dragItem); + if (indexOfItemUnderMouseToDrop != -1) + { + int indexOfPrimarySelection = 0; + if (selectedItem != null) + { + indexOfPrimarySelection = parentToolStrip.Items.IndexOf(selectedItem); + } + + if (indexOfPrimarySelection != -1 && indexOfItemUnderMouseToDrop > indexOfPrimarySelection) + { + indexOfItemUnderMouseToDrop--; + } + foreach (ToolStripItem item in components) + { + parentToolStrip.Items.Insert(indexOfItemUnderMouseToDrop, item); + } + } + selSvc.SetSelectedComponents(new IComponent[] { selectedItem }, SelectionTypes.Primary | SelectionTypes.Replace); + } + } + if (changeSvc != null) + { + ToolStripDropDown dropDown = parentToolStrip as ToolStripDropDown; + if (dropDown != null) + { + ToolStripItem ownerItem = dropDown.OwnerItem; + changeSvc.OnComponentChanged(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"], null, null); + } + else + { + changeSvc.OnComponentChanged(parentToolStrip, TypeDescriptor.GetProperties(parentToolStrip)["Items"], null, null); + } + + //fire extra changing/changed events. + if (copy) + { + if (dropDown != null) + { + ToolStripItem ownerItem = dropDown.OwnerItem; + changeSvc.OnComponentChanging(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"]); + changeSvc.OnComponentChanged(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"], null, null); + } + else + { + changeSvc.OnComponentChanging(parentToolStrip, TypeDescriptor.GetProperties(parentToolStrip)["Items"]); + changeSvc.OnComponentChanged(parentToolStrip, TypeDescriptor.GetProperties(parentToolStrip)["Items"], null, null); + } + } + } + + //If Parent is DropDown... we have to manage the Glyphs .... + foreach (ToolStripItem item in components) + { + if (item is ToolStripDropDownItem) + { + if (designerHost.GetDesigner(item) is ToolStripMenuItemDesigner itemDesigner) + { + itemDesigner.InitializeDropDown(); + } + } + if (item.GetCurrentParent() is ToolStripDropDown dropDown && !(dropDown is ToolStripOverflow)) + { + if (dropDown.OwnerItem is ToolStripDropDownItem ownerItem) + { + if (designerHost.GetDesigner(ownerItem) is ToolStripMenuItemDesigner ownerDesigner) + { + ownerDesigner.InitializeBodyGlyphsForItems(false, ownerItem); + ownerDesigner.InitializeBodyGlyphsForItems(true, ownerItem); + } + } + } + } + // Refresh on SelectionManager... + BehaviorService bSvc = GetBehaviorService(currentDropItem); + if (bSvc != null) + { + bSvc.SyncSelection(); + } + } + catch (Exception ex) + { + if (designerTransaction != null) + { + designerTransaction.Cancel(); + designerTransaction = null; + } + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + finally + { + if (designerTransaction != null) + { + designerTransaction.Commit(); + designerTransaction = null; + } + } + } + } + } + + /// + /// OnDragEnter can be overridden so that a Behavior can specify its own Drag/Drop rules. + /// + public override void OnDragEnter(Glyph g, DragEventArgs e) + { + ToolStripItemGlyph glyph = g as ToolStripItemGlyph; + ToolStripItem glyphItem = glyph.Item; + if (e.Data is ToolStripItemDataObject data) + { + // support move only within same container. + if (data.Owner == glyphItem.Owner) + { + PaintInsertionMark(glyphItem); + ToolStripDesigner.s_dragItem = glyphItem; + e.Effect = DragDropEffects.Move; + } + else + { + e.Effect = DragDropEffects.None; + } + } + else + { + e.Effect = DragDropEffects.None; + } + } + + /// + /// OnDragLeave can be overridden so that a Behavior can specify its own Drag/Drop rules. + /// + public override void OnDragLeave(Glyph g, EventArgs e) + { + ToolStripItemGlyph glyph = g as ToolStripItemGlyph; + ClearInsertionMark(glyph.Item); + } + + /// + /// OnDragOver can be overridden so that a Behavior can specify its own Drag/Drop rules. + /// + public override void OnDragOver(Glyph g, DragEventArgs e) + { + // Determine whether string data exists in the drop data. If not, then the drop effect reflects that the drop cannot occur. + ToolStripItemGlyph glyph = g as ToolStripItemGlyph; + ToolStripItem glyphItem = glyph.Item; + if (e.Data is ToolStripItemDataObject) + { + PaintInsertionMark(glyphItem); + e.Effect = (Control.ModifierKeys == Keys.Control) ? DragDropEffects.Copy : DragDropEffects.Move; + } + else + { + e.Effect = DragDropEffects.None; + } + } + + /// + /// Paints the insertion mark when items are being reordered + /// + private void PaintInsertionMark(ToolStripItem item) + { + // Dont paint if cursor hasnt moved. + if (ToolStripDesigner.s_lastCursorPosition != Point.Empty && ToolStripDesigner.s_lastCursorPosition == Cursor.Position) + { + return; + } + // Dont paint any "MouseOver" glyohs if TemplateNode is ACTIVE ! + ToolStripKeyboardHandlingService keyService = GetKeyBoardHandlingService(item); + if (keyService != null && keyService.TemplateNodeActive) + { + return; + } + + //Start from fresh State... + if (item != null && item.Site != null) + { + ToolStripDesigner.s_lastCursorPosition = Cursor.Position; + IDesignerHost designerHost = (IDesignerHost)item.Site.GetService(typeof(IDesignerHost)); + if (designerHost != null) + { + Rectangle bounds = GetPaintingBounds(designerHost, item); + BehaviorService bSvc = GetBehaviorService(item); + if (bSvc != null) + { + Graphics g = bSvc.AdornerWindowGraphics; + try + { + using (Pen p = new Pen(new SolidBrush(Color.Black))) + { + p.DashStyle = DashStyle.Dot; + g.DrawRectangle(p, bounds); + } + } + finally + { + g.Dispose(); + } + } + } + } + } + + /// + /// QueryContinueDrag can be overridden so that a Behavior can specify its own Drag/Drop rules. + /// + private void QueryContinueDrag(object sender, QueryContinueDragEventArgs e) + { + // Cancel the drag if the mouse moves off the form. + if (e.Action == DragAction.Continue) + { + return; + } + if (e.EscapePressed) + { + e.Action = DragAction.Cancel; + ToolStripItem item = sender as ToolStripItem; + SetParentDesignerValuesForDragDrop(item, false, Point.Empty); + ISelectionService selSvc = GetSelectionService(item); + if (selSvc != null) + { + selSvc.SetSelectedComponents(new IComponent[] { item }, SelectionTypes.Auto); + } + ToolStripDesigner.s_dragItem = null; + } + } + + // Set values before initiating the Drag-Drop + private void SetParentDesignerValuesForDragDrop(ToolStripItem glyphItem, bool setValues, Point mouseLoc) + { + if (glyphItem.Site == null) + { + return; + } + // Remember the point where the mouse down occurred. The DragSize indicates the size that the mouse can move before a drag event should be started. + Size dragSize = new Size(1, 1); + + IDesignerHost designerHost = (IDesignerHost)glyphItem.Site.GetService(typeof(IDesignerHost)); + Debug.Assert(designerHost != null, "Invalid DesignerHost"); + + + // implement Drag Drop for individual ToolStrip Items While this item is getting selected.. Get the index of the item the mouse is below. + if (glyphItem.Placement == ToolStripItemPlacement.Overflow || (glyphItem.Placement == ToolStripItemPlacement.Main && !(glyphItem.IsOnDropDown))) + { + ToolStripItemDesigner itemDesigner = designerHost.GetDesigner(glyphItem) as ToolStripItemDesigner; + ToolStrip parentToolStrip = itemDesigner.GetMainToolStrip(); + if (designerHost.GetDesigner(parentToolStrip) is ToolStripDesigner parentDesigner) + { + if (setValues) + { + parentDesigner.IndexOfItemUnderMouseToDrag = parentToolStrip.Items.IndexOf(glyphItem); + // Create a rectangle using the DragSize, with the mouse position being at the center of the rectangle. On SelectionChanged we recreate the Glyphs ... so need to stash this value on the parentDesigner.... + parentDesigner.DragBoxFromMouseDown = _dragBoxFromMouseDown = new Rectangle(new Point(mouseLoc.X - (dragSize.Width / 2), mouseLoc.Y - (dragSize.Height / 2)), dragSize); + } + else + { + parentDesigner.IndexOfItemUnderMouseToDrag = -1; + parentDesigner.DragBoxFromMouseDown = _dragBoxFromMouseDown = Rectangle.Empty; + } + } + } + else if (glyphItem.IsOnDropDown) + { + //Get the OwnerItem's Designer and set the value... + if (glyphItem.Owner is ToolStripDropDown parentDropDown) + { + ToolStripItem ownerItem = parentDropDown.OwnerItem; + if (designerHost.GetDesigner(ownerItem) is ToolStripItemDesigner ownerItemDesigner) + { + if (setValues) + { + ownerItemDesigner.indexOfItemUnderMouseToDrag = parentDropDown.Items.IndexOf(glyphItem); + // Create a rectangle using the DragSize, with the mouse position being at the center of the rectangle. On SelectionChanged we recreate the Glyphs ... so need to stash this value on the parentDesigner.... + ownerItemDesigner.dragBoxFromMouseDown = _dragBoxFromMouseDown = new Rectangle(new Point(mouseLoc.X - (dragSize.Width / 2), mouseLoc.Y - (dragSize.Height / 2)), dragSize); + } + else + { + ownerItemDesigner.indexOfItemUnderMouseToDrag = -1; + ownerItemDesigner.dragBoxFromMouseDown = _dragBoxFromMouseDown = Rectangle.Empty; + } + } + } + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs new file mode 100644 index 00000000000..253557fa872 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs @@ -0,0 +1,701 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; + +namespace System.Windows.Forms.Design +{ + /// + /// Custom ContextMenu section for ToolStripMenuItems. + /// + internal class ToolStripItemCustomMenuItemCollection : CustomMenuItemCollection + { + private ToolStripItem currentItem; + private IServiceProvider serviceProvider; + + private ToolStripMenuItem imageToolStripMenuItem; + private ToolStripMenuItem enabledToolStripMenuItem; + + private ToolStripMenuItem isLinkToolStripMenuItem; + private ToolStripMenuItem springToolStripMenuItem; + + private ToolStripMenuItem checkedToolStripMenuItem; + private ToolStripMenuItem showShortcutKeysToolStripMenuItem; + + private ToolStripMenuItem alignmentToolStripMenuItem; + private ToolStripMenuItem displayStyleToolStripMenuItem; + + private ToolStripSeparator toolStripSeparator1; + + private ToolStripMenuItem convertToolStripMenuItem; + private ToolStripMenuItem insertToolStripMenuItem; + + + private ToolStripMenuItem leftToolStripMenuItem; + private ToolStripMenuItem rightToolStripMenuItem; + + private ToolStripMenuItem noneStyleToolStripMenuItem; + private ToolStripMenuItem textStyleToolStripMenuItem; + private ToolStripMenuItem imageStyleToolStripMenuItem; + private ToolStripMenuItem imageTextStyleToolStripMenuItem; + + private ToolStripMenuItem editItemsToolStripMenuItem; + private CollectionEditVerbManager verbManager; + + public ToolStripItemCustomMenuItemCollection(IServiceProvider provider, Component currentItem) : base() + { + serviceProvider = provider; + this.currentItem = currentItem as ToolStripItem; + PopulateList(); + } + + /// + /// Parent ToolStrip. + /// + private ToolStrip ParentTool + { + get => currentItem.Owner; + } + + /// + /// creates a item representing an item, respecting Browsable. + /// + private ToolStripMenuItem CreatePropertyBasedItem(string text, string propertyName, string imageName) + { + ToolStripMenuItem item = new ToolStripMenuItem(text); + bool browsable = IsPropertyBrowsable(propertyName); + item.Visible = browsable; + if (browsable) + { + if (!string.IsNullOrEmpty(imageName)) + { + item.Image = new Bitmap(BitmapSelector.GetResourceStream(typeof(ToolStripMenuItem), imageName)); + item.ImageTransparentColor = Color.Magenta; + } + + if (serviceProvider.GetService(typeof(IUIService)) is IUIService uis) + { + item.DropDown.Renderer = (ToolStripProfessionalRenderer)uis.Styles["VsRenderer"]; + item.DropDown.Font = (Font)uis.Styles["DialogFont"]; + } + } + return item; + } + + /// + /// creates an item that when clicked changes the enum value. + /// + private ToolStripMenuItem CreateEnumValueItem(string propertyName, string name, object value) + { + ToolStripMenuItem item = new ToolStripMenuItem(name); + item.Tag = new EnumValueDescription(propertyName, value); + item.Click += new EventHandler(OnEnumValueChanged); + return item; + } + + private ToolStripMenuItem CreateBooleanItem(string text, string propertyName) + { + ToolStripMenuItem item = new ToolStripMenuItem(text); + bool browsable = IsPropertyBrowsable(propertyName); + item.Visible = browsable; + item.Tag = propertyName; + item.CheckOnClick = true; + item.Click += new EventHandler(OnBooleanValueChanged); + return item; + } + + // Property names are hard-coded intentionally + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + private void PopulateList() + { + ToolStripItem selectedItem = currentItem; + if (!(selectedItem is ToolStripControlHost) && !(selectedItem is ToolStripSeparator)) + { + imageToolStripMenuItem = new ToolStripMenuItem(); + imageToolStripMenuItem.Text = SR.ToolStripItemContextMenuSetImage; + imageToolStripMenuItem.Image = new Bitmap(typeof(ToolStripMenuItem), "image.bmp"); + imageToolStripMenuItem.ImageTransparentColor = Color.Magenta; + //Add event Handlers + imageToolStripMenuItem.Click += new EventHandler(OnImageToolStripMenuItemClick); + enabledToolStripMenuItem = CreateBooleanItem("E&nabled", "Enabled"); + this.AddRange(new ToolStripItem[] { imageToolStripMenuItem, enabledToolStripMenuItem}); + if (selectedItem is ToolStripMenuItem) + { + checkedToolStripMenuItem = CreateBooleanItem("C&hecked", "Checked"); + showShortcutKeysToolStripMenuItem = CreateBooleanItem("ShowShortcut&Keys", "ShowShortcutKeys"); + this.AddRange(new System.Windows.Forms.ToolStripItem[] { checkedToolStripMenuItem, showShortcutKeysToolStripMenuItem}); + } + else + { + if (selectedItem is ToolStripLabel) + { + isLinkToolStripMenuItem = CreateBooleanItem("IsLin&k", "IsLink"); + this.Add(isLinkToolStripMenuItem); + } + + if (selectedItem is ToolStripStatusLabel) + { + springToolStripMenuItem = CreateBooleanItem("Sprin&g", "Spring"); + this.Add(springToolStripMenuItem); + } + + leftToolStripMenuItem = CreateEnumValueItem("Alignment", "Left", ToolStripItemAlignment.Left); + rightToolStripMenuItem = CreateEnumValueItem("Alignment", "Right", ToolStripItemAlignment.Right); + noneStyleToolStripMenuItem = CreateEnumValueItem("DisplayStyle", "None", ToolStripItemDisplayStyle.None); + textStyleToolStripMenuItem = CreateEnumValueItem("DisplayStyle", "Text", ToolStripItemDisplayStyle.Text); + imageStyleToolStripMenuItem = CreateEnumValueItem("DisplayStyle", "Image", ToolStripItemDisplayStyle.Image); + imageTextStyleToolStripMenuItem = CreateEnumValueItem("DisplayStyle", "ImageAndText", ToolStripItemDisplayStyle.ImageAndText); + // alignmentToolStripMenuItem + alignmentToolStripMenuItem = CreatePropertyBasedItem("Ali&gnment", "Alignment", "alignment.bmp"); + alignmentToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { leftToolStripMenuItem, rightToolStripMenuItem}); + // displayStyleToolStripMenuItem + displayStyleToolStripMenuItem = CreatePropertyBasedItem("Displa&yStyle", "DisplayStyle", "displaystyle.bmp"); + displayStyleToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { noneStyleToolStripMenuItem, textStyleToolStripMenuItem, imageStyleToolStripMenuItem, imageTextStyleToolStripMenuItem}); + + if (serviceProvider.GetService(typeof(IUIService)) is IUIService uis) + { + // We already have code which expects VsRenderer and DialogFont to be always available without the need for null checks + ToolStripProfessionalRenderer renderer = (ToolStripProfessionalRenderer)uis.Styles["VsRenderer"]; + alignmentToolStripMenuItem.DropDown.Renderer = renderer; + displayStyleToolStripMenuItem.DropDown.Renderer = renderer; + + Font font = (Font)uis.Styles["DialogFont"]; + alignmentToolStripMenuItem.DropDown.Font = font; + displayStyleToolStripMenuItem.DropDown.Font = font; + + // VsColorPanelText may be undefined, so we do need the check for Color here + object panelTextObject = uis.Styles["VsColorPanelText"]; + if (panelTextObject is Color panelTextColor) + { + alignmentToolStripMenuItem.DropDown.ForeColor = panelTextColor; + displayStyleToolStripMenuItem.DropDown.ForeColor = panelTextColor; + } + } + this.AddRange(new System.Windows.Forms.ToolStripItem[] { alignmentToolStripMenuItem, displayStyleToolStripMenuItem, }); + } + toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.Add(toolStripSeparator1); + } + + convertToolStripMenuItem = new ToolStripMenuItem + { + Text = SR.ToolStripItemContextMenuConvertTo, + DropDown = ToolStripDesignerUtils.GetNewItemDropDown(ParentTool, currentItem, new EventHandler(AddNewItemClick), true, serviceProvider, true) + }; + insertToolStripMenuItem = new ToolStripMenuItem + { + Text = SR.ToolStripItemContextMenuInsert, + DropDown = ToolStripDesignerUtils.GetNewItemDropDown(ParentTool, currentItem, new EventHandler(AddNewItemClick), false, serviceProvider, true) + }; + + this.AddRange(new System.Windows.Forms.ToolStripItem[] { convertToolStripMenuItem, insertToolStripMenuItem}); + + if (currentItem is ToolStripDropDownItem) + { + IDesignerHost _designerHost = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost)); + if (_designerHost != null) + { + if (_designerHost.GetDesigner(currentItem) is ToolStripItemDesigner itemDesigner) + { + verbManager = new CollectionEditVerbManager(string.Format(SR.ToolStripDropDownItemCollectionEditorVerb), itemDesigner, TypeDescriptor.GetProperties(currentItem)["DropDownItems"], false); + editItemsToolStripMenuItem = new ToolStripMenuItem(); + editItemsToolStripMenuItem.Text = SR.ToolStripDropDownItemCollectionEditorVerb; + editItemsToolStripMenuItem.Click += new EventHandler(OnEditItemsMenuItemClick); + editItemsToolStripMenuItem.Image = new Bitmap(BitmapSelector.GetResourceStream(typeof(ToolStripMenuItem), "editdropdownlist.bmp")); + editItemsToolStripMenuItem.ImageTransparentColor = Color.Magenta; + this.Add(editItemsToolStripMenuItem); + } + } + } + } + + private void OnEditItemsMenuItemClick(object sender, EventArgs e) + { + if (verbManager != null) + { + verbManager.EditItemsVerb.Invoke(); + } + } + + private void OnImageToolStripMenuItemClick(object sender, EventArgs e) + { + IDesignerHost _designerHost = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost)); + if (_designerHost != null) + { + if (_designerHost.GetDesigner(currentItem) is ToolStripItemDesigner itemDesigner) + { + try + { + // EditorServiceContext will check if the user has changed the property and set it for us. + EditorServiceContext.EditValue(itemDesigner, currentItem, "Image"); + } + catch (InvalidOperationException ex) + { + IUIService uiService = (IUIService)serviceProvider.GetService(typeof(IUIService)); + uiService.ShowError(ex.Message); + } + } + } + } + + private void OnBooleanValueChanged(object sender, EventArgs e) + { + ToolStripItem item = sender as ToolStripItem; + Debug.Assert(item != null, "Why is item null?"); + if (item != null) + { + string propertyName = item.Tag as string; + Debug.Assert(propertyName != null, "Why is propertyName null?"); + if (propertyName != null) + { + bool currentValue = (bool)GetProperty(propertyName); + ChangeProperty(propertyName, !currentValue); + } + } + } + + private void OnEnumValueChanged(object sender, EventArgs e) + { + ToolStripItem item = sender as ToolStripItem; + Debug.Assert(item != null, "Why is item null?"); + if (item != null) + { + EnumValueDescription desc = item.Tag as EnumValueDescription; + Debug.Assert(desc != null, "Why is desc null?"); + if (desc != null && !string.IsNullOrEmpty(desc.PropertyName)) + { + ChangeProperty(desc.PropertyName, desc.Value); + } + } + } + + private void AddNewItemClick(object sender, EventArgs e) + { + ItemTypeToolStripMenuItem senderItem = (ItemTypeToolStripMenuItem)sender; + Type t = senderItem.ItemType; + if (senderItem.ConvertTo) + { + //we are morphing the currentItem + MorphToolStripItem(t); + } + else + { + // we are inserting a new item.. + InsertItem(t); + } + } + + private void MorphToolStripItem(Type t) + { + // Go thru morphing routine only if we have different type. + if (t != currentItem.GetType()) + { + IDesignerHost _designerHost = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost)); + ToolStripItemDesigner _designer = (ToolStripItemDesigner)_designerHost.GetDesigner(currentItem); + _designer.MorphCurrentItem(t); + } + } + + private void InsertItem(Type t) + { + if (currentItem is ToolStripMenuItem) + { + InsertMenuItem(t); + } + else + { + InsertStripItem(t); + } + } + + /// + /// Insert MenuItem into ToolStrip. + /// + private void InsertStripItem(Type t) + { + if (ParentTool is StatusStrip parent) + { + InsertIntoStatusStrip(parent, t); + } + else + { + InsertToolStripItem(t); + } + } + + /// + /// Insert MenuItem into ToolStrip. + /// + private void InsertMenuItem(Type t) + { + if (ParentTool is MenuStrip parent) + { + InsertIntoMainMenu(parent, t); + } + else + { + InsertIntoDropDown((ToolStripDropDown)currentItem.Owner, t); + } + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private void TryCancelTransaction(ref DesignerTransaction transaction) + { + if (transaction != null) + { + try + { + transaction.Cancel(); + transaction = null; + } + catch + { + } + } + } + + /// + /// Insert Item into DropDownMenu. + /// + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + private void InsertIntoDropDown(ToolStripDropDown parent, Type t) + { + IDesignerHost designerHost = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost)); + Debug.Assert(designerHost != null, "Why didn't we get a designer host?"); + int dummyIndex = parent.Items.IndexOf(currentItem); + if (parent != null) + { + if (parent.OwnerItem is ToolStripDropDownItem ownerItem) + { + if (ownerItem.DropDownDirection == ToolStripDropDownDirection.AboveLeft || ownerItem.DropDownDirection == ToolStripDropDownDirection.AboveRight) + { + dummyIndex++; + } + } + + } + + DesignerTransaction newItemTransaction = designerHost.CreateTransaction(SR.ToolStripAddingItem); + try + { + // the code in ComponentAdded will actually get the add done. + IComponent component = designerHost.CreateComponent(t); + IDesigner designer = designerHost.GetDesigner(component); + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + + parent.Items.Insert(dummyIndex, (ToolStripItem)component); + // set the selection to our new item.. since we destroyed Original component.. we have to ask SelectionServive from new Component + ISelectionService selSvc = (ISelectionService)serviceProvider.GetService(typeof(ISelectionService)); + if (selSvc != null) + { + selSvc.SetSelectedComponents(new object[] { component }, SelectionTypes.Replace); + } + } + catch (Exception ex) + { + // We need to cancel the ToolStripDesigner's nested MenuItemTransaction; otherwise, we can't cancel our Transaction and the Designer will be left in an unusable state + if ((parent != null) && (parent.OwnerItem != null) && (parent.OwnerItem.Owner != null)) + { + ToolStripDesigner toolStripDesigner = designerHost.GetDesigner(parent.OwnerItem.Owner) as ToolStripDesigner; + if (toolStripDesigner != null) + { + toolStripDesigner.CancelPendingMenuItemTransaction(); + } + } + + // Cancel our new Item transaction + TryCancelTransaction(ref newItemTransaction); + + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + finally + { + if (newItemTransaction != null) + { + newItemTransaction.Commit(); + newItemTransaction = null; + } + } + } + + /// + /// Insert Item into Main MenuStrip. + /// + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + private void InsertIntoMainMenu(MenuStrip parent, Type t) + { + IDesignerHost designerHost = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost)); + Debug.Assert(designerHost != null, "Why didn't we get a designer host?"); + int dummyIndex = parent.Items.IndexOf(currentItem); + DesignerTransaction newItemTransaction = designerHost.CreateTransaction(SR.ToolStripAddingItem); + try + { + // the code in ComponentAdded will actually get the add done. + IComponent component = designerHost.CreateComponent(t); + IDesigner designer = designerHost.GetDesigner(component); + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + Debug.Assert(dummyIndex != -1, "Why is item index negative?"); + parent.Items.Insert(dummyIndex, (ToolStripItem)component); + // set the selection to our new item.. since we destroyed Original component.. we have to ask SelectionServive from new Component + ISelectionService selSvc = (ISelectionService)serviceProvider.GetService(typeof(ISelectionService)); + if (selSvc != null) + { + selSvc.SetSelectedComponents(new object[] { component }, SelectionTypes.Replace); + } + } + catch (Exception ex) + { + TryCancelTransaction(ref newItemTransaction); + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + finally + { + if (newItemTransaction != null) + { + newItemTransaction.Commit(); + newItemTransaction = null; + } + } + } + + /// + /// Insert Item into StatusStrip. + /// + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + private void InsertIntoStatusStrip(StatusStrip parent, Type t) + { + IDesignerHost designerHost = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost)); + Debug.Assert(designerHost != null, "Why didn't we get a designer host?"); + int dummyIndex = parent.Items.IndexOf(currentItem); + DesignerTransaction newItemTransaction = designerHost.CreateTransaction(SR.ToolStripAddingItem); + try + { + // the code in ComponentAdded will actually get the add done. + IComponent component = designerHost.CreateComponent(t); + IDesigner designer = designerHost.GetDesigner(component); + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + Debug.Assert(dummyIndex != -1, "Why is item index negative?"); + parent.Items.Insert(dummyIndex, (ToolStripItem)component); + // set the selection to our new item.. since we destroyed Original component.. we have to ask SelectionServive from new Component + ISelectionService selSvc = (ISelectionService)serviceProvider.GetService(typeof(ISelectionService)); + if (selSvc != null) + { + selSvc.SetSelectedComponents(new object[] { component }, SelectionTypes.Replace); + } + } + catch (Exception ex) + { + TryCancelTransaction(ref newItemTransaction); + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + finally + { + if (newItemTransaction != null) + { + newItemTransaction.Commit(); + newItemTransaction = null; + } + } + } + + /// + /// Insert Item into ToolStrip. + /// + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + private void InsertToolStripItem(Type t) + { + IDesignerHost designerHost = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost)); + Debug.Assert(designerHost != null, "Why didn't we get a designer host?"); + ToolStrip parent = ParentTool; + int dummyIndex = parent.Items.IndexOf(currentItem); + DesignerTransaction newItemTransaction = designerHost.CreateTransaction(SR.ToolStripAddingItem); + try + { + // the code in ComponentAdded will actually get the add done. + IComponent component = designerHost.CreateComponent(t); + IDesigner designer = designerHost.GetDesigner(component); + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + //Set the Image property and DisplayStyle... + if (component is ToolStripButton || component is ToolStripSplitButton || component is ToolStripDropDownButton) + { + Image image = null; + try + { + image = new Bitmap(typeof(ToolStripButton), "blank.bmp"); + } + catch (Exception ex) + { + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + ChangeProperty(component, "Image", image); + ChangeProperty(component, "DisplayStyle", ToolStripItemDisplayStyle.Image); + ChangeProperty(component, "ImageTransparentColor", Color.Magenta); + } + + Debug.Assert(dummyIndex != -1, "Why is item index negative?"); + parent.Items.Insert(dummyIndex, (ToolStripItem)component); + // set the selection to our new item.. since we destroyed Original component.. we have to ask SelectionServive from new Component + ISelectionService selSvc = (ISelectionService)serviceProvider.GetService(typeof(ISelectionService)); + if (selSvc != null) + { + selSvc.SetSelectedComponents(new object[] { component }, SelectionTypes.Replace); + } + } + catch (Exception ex) + { + if (newItemTransaction != null) + { + newItemTransaction.Cancel(); + newItemTransaction = null; + } + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + + finally + { + if (newItemTransaction != null) + { + newItemTransaction.Commit(); + newItemTransaction = null; + } + } + } + + private bool IsPropertyBrowsable(string propertyName) + { + PropertyDescriptor getProperty = TypeDescriptor.GetProperties(currentItem)[propertyName]; + Debug.Assert(getProperty != null, "Could not find given property in control."); + if (getProperty != null) + { + if (getProperty.Attributes[typeof(BrowsableAttribute)] is BrowsableAttribute attribute) + { + return attribute.Browsable; + } + } + return true; + } + + + //helper function to get the property on the actual Control + private object GetProperty(string propertyName) + { + PropertyDescriptor getProperty = TypeDescriptor.GetProperties(currentItem)[propertyName]; + Debug.Assert(getProperty != null, "Could not find given property in control."); + if (getProperty != null) + { + return getProperty.GetValue(currentItem); + } + return null; + } + + //helper function to change the property on the actual Control + protected void ChangeProperty(string propertyName, object value) + { + ChangeProperty(currentItem, propertyName, value); + } + + protected void ChangeProperty(IComponent target, string propertyName, object value) + { + PropertyDescriptor changingProperty = TypeDescriptor.GetProperties(target)[propertyName]; + Debug.Assert(changingProperty != null, "Could not find given property in control."); + try + { + if (changingProperty != null) + { + changingProperty.SetValue(target, value); + } + } + catch (InvalidOperationException ex) + { + IUIService uiService = (IUIService)serviceProvider.GetService(typeof(IUIService)); + uiService.ShowError(ex.Message); + } + } + + private void RefreshAlignment() + { + ToolStripItemAlignment currentAlignmentValue = (ToolStripItemAlignment)GetProperty("Alignment"); + leftToolStripMenuItem.Checked = (currentAlignmentValue == ToolStripItemAlignment.Left) ? true : false; + rightToolStripMenuItem.Checked = (currentAlignmentValue == ToolStripItemAlignment.Right) ? true : false; + } + + private void RefreshDisplayStyle() + { + ToolStripItemDisplayStyle currentDisplayStyleValue = (ToolStripItemDisplayStyle)GetProperty("DisplayStyle"); + noneStyleToolStripMenuItem.Checked = (currentDisplayStyleValue == ToolStripItemDisplayStyle.None) ? true : false; + textStyleToolStripMenuItem.Checked = (currentDisplayStyleValue == ToolStripItemDisplayStyle.Text) ? true : false; + imageStyleToolStripMenuItem.Checked = (currentDisplayStyleValue == ToolStripItemDisplayStyle.Image) ? true : false; + imageTextStyleToolStripMenuItem.Checked = (currentDisplayStyleValue == ToolStripItemDisplayStyle.ImageAndText) ? true : false; + } + + public override void RefreshItems() + { + base.RefreshItems(); + ToolStripItem selectedItem = currentItem; + if (!(selectedItem is ToolStripControlHost) && !(selectedItem is ToolStripSeparator)) + { + enabledToolStripMenuItem.Checked = (bool)GetProperty("Enabled"); + if (selectedItem is ToolStripMenuItem) + { + checkedToolStripMenuItem.Checked = (bool)GetProperty("Checked"); + showShortcutKeysToolStripMenuItem.Checked = (bool)GetProperty("ShowShortcutKeys"); + } + else + { + if (selectedItem is ToolStripLabel) + { + isLinkToolStripMenuItem.Checked = (bool)GetProperty("IsLink"); + } + RefreshAlignment(); + RefreshDisplayStyle(); + } + } + } + + // tiny little class to handle enum value changes + private class EnumValueDescription + { + public EnumValueDescription(string propertyName, object value) + { + PropertyName = propertyName; + Value = value; + } + public string PropertyName; + public object Value; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemDataObject.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemDataObject.cs new file mode 100644 index 00000000000..c46d9fb877f --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemDataObject.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; + +namespace System.Windows.Forms.Design +{ + /// + /// Wrapper class for DataObject. This wrapped object is passed when a ToolStripItem is Drag-Dropped during DesignTime. + /// + internal class ToolStripItemDataObject : DataObject + { + private readonly ArrayList _dragComponents; + private readonly ToolStrip _owner; + private readonly ToolStripItem _primarySelection; + internal ToolStripItemDataObject(ArrayList dragComponents, ToolStripItem primarySelection, ToolStrip owner) : base() + { + _dragComponents = dragComponents; + _owner = owner; + _primarySelection = primarySelection; + } + + internal ArrayList DragComponents + { + get => _dragComponents; + } + + internal ToolStrip Owner + { + get => _owner; + } + + internal ToolStripItem PrimarySelection + { + get => _primarySelection; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemDesigner.cs new file mode 100644 index 00000000000..7dabbcda71d --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemDesigner.cs @@ -0,0 +1,1360 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.ComponentModel.Design.Serialization; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + internal class ToolStripItemDesigner : ComponentDesigner + { + private const int GLYPHBORDER = 1; + private const int GLYPHINSET = 2; + // Cached in value of the TemplateNode (which is the InSitu Editor) + private ToolStripTemplateNode _editorNode; + // Used by the ParentDesigner (ToolStripDesigner) to know whether there is any active Editor. + private bool isEditorActive = false; + // this property is used in the InitializeNewComponent not to set the text for the ToolstripItem + private bool internalCreate = false; + //hook to SelectionService to listen to SelectionChanged + private ISelectionService selSvc = null; + //ToolStripItems Visibility needs to be WYSIWYG. + private bool currentVisible = false; + private Rectangle lastInsertionMarkRect = Rectangle.Empty; + // Required to remove Body Glyphs .... + internal ControlBodyGlyph bodyGlyph = null; + //bool which is set if we Add Dummy Item + internal bool dummyItemAdded = false; + //Needed to Store the DRAGDROP Rect from the WinbarItemBehavior. + internal Rectangle dragBoxFromMouseDown = Rectangle.Empty; + //defaulted to invalid index. this will be set by the behaviour. + internal int indexOfItemUnderMouseToDrag = -1; + private ToolStripItemCustomMenuItemCollection toolStripItemCustomMenuItemCollection; + + internal bool AutoSize + { + get => (bool)ShadowProperties["AutoSize"]; + set + { + bool autoSize = (bool)ShadowProperties["AutoSize"]; + // always set this in regardless of whether the property changed. it can come back to bite later after in-situ editing if we dont. + ShadowProperties["AutoSize"] = value; + if (value != autoSize) + { + ToolStripItem.AutoSize = value; + } + } + } + + private string AccessibleName + { + get + { + return (string)ShadowProperties["AccessibleName"]; + } + set + { + ShadowProperties["AccessibleName"] = value; + } + } + + /// + /// Associated Parent Designer + /// + internal override bool CanBeAssociatedWith(IDesigner parentDesigner) + { + return (parentDesigner is ToolStripDesigner); + } + + + /// + /// Designer Custom ContextMenu. + /// + private ContextMenuStrip DesignerContextMenu + { + get + { + BaseContextMenuStrip toolStripContextMenu = new BaseContextMenuStrip(Component.Site, ToolStripItem); + // If multiple Items Selected dont show the custom properties... + if (selSvc.SelectionCount > 1) + { + toolStripContextMenu.GroupOrdering.Clear(); + toolStripContextMenu.GroupOrdering.AddRange(new string[] { StandardGroups.Code, StandardGroups.Selection, StandardGroups.Edit, StandardGroups.Properties}); + } + else + { + toolStripContextMenu.GroupOrdering.Clear(); + toolStripContextMenu.GroupOrdering.AddRange(new string[] { StandardGroups.Code, StandardGroups.Custom, StandardGroups.Selection, StandardGroups.Edit, StandardGroups.Properties}); + toolStripContextMenu.Text = "CustomContextMenu"; + if (toolStripItemCustomMenuItemCollection == null) + { + toolStripItemCustomMenuItemCollection = new ToolStripItemCustomMenuItemCollection(Component.Site, ToolStripItem); + } + foreach (ToolStripItem item in toolStripItemCustomMenuItemCollection) + { + toolStripContextMenu.Groups[StandardGroups.Custom].Items.Add(item); + } + } + + // Refresh the list on every show.. + if (toolStripItemCustomMenuItemCollection != null) + { + toolStripItemCustomMenuItemCollection.RefreshItems(); + } + toolStripContextMenu.Populated = false; + return toolStripContextMenu; + } + } + + /// + /// ToolStripEditorManager used this internal property to Activate the editor. + /// + internal virtual ToolStripTemplateNode Editor + { + get => _editorNode; + set => _editorNode = value; + } + + + // ToolStripItems if Inherited ACT as Readonly. + protected override InheritanceAttribute InheritanceAttribute + { + get + { + if ((base.InheritanceAttribute == InheritanceAttribute.Inherited)) + { + return InheritanceAttribute.InheritedReadOnly; + } + return base.InheritanceAttribute; + } + } + + /// + /// ToolStripEditorManager used this internal property to set the the desinger's IsEditorActive to notify if this item has entered or exited the InSitu Edit Mode. + /// + internal bool IsEditorActive + { + get => isEditorActive; + set => isEditorActive = value; + } + + /// + /// When the ToolStripItem is created we dont want InitializeNewComponent to set the "text" we do it ourselves from the Text the User has provided in the InSitu Edit Mode. Reason being the item and the Parent unnecessarily Layout and cause flicker. + /// + internal bool InternalCreate + { + get => internalCreate; + set => internalCreate = value; + } + + protected IComponent ImmediateParent + { + get + { + if (ToolStripItem != null) + { + ToolStrip parent = ToolStripItem.GetCurrentParent(); + return parent ?? ToolStripItem.Owner; + } + return null; + } + } + + private ToolStripItemOverflow Overflow + { + get => (ToolStripItemOverflow)ShadowProperties["Overflow"]; + set + { + // first Hide the Overflow.. + if (ToolStripItem.IsOnOverflow) + { + ToolStrip strip = ToolStripItem.Owner as ToolStrip; + if (strip.OverflowButton.DropDown.Visible) + { + strip.OverflowButton.HideDropDown(); + } + } + if (ToolStripItem is ToolStripDropDownItem) + { + ToolStripDropDownItem item = ToolStripItem as ToolStripDropDownItem; + item.HideDropDown(); + } + //set the value on actual item + if (value != ToolStripItem.Overflow) + { + ToolStripItem.Overflow = value; + ShadowProperties["Overflow"] = value; + } + + // Since this cause the whole Layout to Change ... Call SyncSelection to reset the glyphs... + BehaviorService b = (BehaviorService)GetService(typeof(BehaviorService)); + if (b != null) + { + b.SyncSelection(); + } + } + } + + protected override IComponent ParentComponent + { + get + { + if (ToolStripItem != null) + { + if (ToolStripItem.IsOnDropDown && !ToolStripItem.IsOnOverflow) + { + if (ImmediateParent is ToolStripDropDown parentDropDown) + { + if (parentDropDown.IsAutoGenerated) + { + return parentDropDown.OwnerItem; + } + else + { + return parentDropDown; + } + } + } + return GetMainToolStrip(); + } + return null; + } + } + + /// + /// Easy method for getting to the ToolStripItem + /// + public ToolStripItem ToolStripItem + { + get => (ToolStripItem)Component; + } + + protected bool Visible + { + get => (bool)ShadowProperties["Visible"]; + set + { + ShadowProperties["Visible"] = value; + currentVisible = value; + } + } + + /// + /// This method adds the Parent Hierarchy to arraylist and returns that arraylist to the Base ContextMenu provider. This way the ToolStripItem can show the right parents in the contextMenu + /// + internal ArrayList AddParentTree() + { + ArrayList parentControls = new ArrayList(); + IDesignerHost designerHost = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (designerHost != null) + { + IComponent root = designerHost.RootComponent; + Component startComp = ToolStripItem; + if (startComp != null && root != null) + { + while (startComp != root) + { + if (startComp is ToolStripItem) + { + ToolStripItem item = startComp as ToolStripItem; + if (item.IsOnDropDown) + { + if (item.IsOnOverflow) + { + parentControls.Add(item.Owner); + startComp = item.Owner; + } + else + { + if (item.Owner is ToolStripDropDown parentDropDown) + { + ToolStripItem ownerItem = parentDropDown.OwnerItem; + if (ownerItem != null) + { + parentControls.Add(ownerItem); + startComp = ownerItem; + } + } + } + } + else + { + if (item.Owner.Site != null) + { + parentControls.Add(item.Owner); + } + startComp = item.Owner; + } + } + else if (startComp is Control) + { + Control selectedControl = startComp as Control; + Control parentControl = selectedControl.Parent; + if (parentControl.Site != null) + { + parentControls.Add(parentControl); + } + startComp = parentControl; + } + } + } + } + return parentControls; + } + + /// + /// Creates the InSitu Edit Node (which is called the TemplateNode). + /// + private void CreateDummyNode() + { + _editorNode = new ToolStripTemplateNode(ToolStripItem, ToolStripItem.Text, ToolStripItem.Image); + } + + /// + /// This is called by the TemplateNode to Commit the Edit. This Function Simply changes the "Text and Image" property of the current ToolStripItem. + /// + // Standard 'catch all - rethrow critical' exception pattern + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + internal virtual void CommitEdit(Type type, string text, bool commit, bool enterKeyPressed, bool tabKeyPressed) + { + ToolStripItem newItem = null; + SelectionManager selMgr = (SelectionManager)GetService(typeof(SelectionManager)); + BehaviorService bSvc = (BehaviorService)GetService(typeof(BehaviorService)); + ToolStrip immediateParent = ImmediateParent as ToolStrip; + immediateParent.SuspendLayout(); + HideDummyNode(); + IDesignerHost designerHost = (IDesignerHost)GetService(typeof(IDesignerHost)); + ToolStripDesigner designer = (ToolStripDesigner)designerHost.GetDesigner(ToolStripItem.Owner); + if (designer != null && designer.EditManager != null) + { + designer.EditManager.ActivateEditor(null, false); + } + // Cannot Add ToolStripSeparator to MenuStrip + if (immediateParent is MenuStrip && type == typeof(ToolStripSeparator)) + { + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host != null) + { + IUIService uiSvc = (IUIService)host.GetService(typeof(IUIService)); + if (uiSvc != null) + { + uiSvc.ShowError(SR.ToolStripSeparatorError); + // dont commit the item.. + commit = false; + // Select the MenuStrip + if (selSvc != null) + { + selSvc.SetSelectedComponents(new object[] { immediateParent }); + } + } + } + } + if (commit) + { + if (dummyItemAdded) + { + try + { + RemoveItem(); + newItem = designer.AddNewItem(type, text, enterKeyPressed, false /* Dont select the templateNode but select the newly added item */); + } + finally + { + if (designer.NewItemTransaction != null) + { + designer.NewItemTransaction.Commit(); + designer.NewItemTransaction = null; + } + } + } + else + { + //create our transaction + DesignerTransaction designerTransaction = designerHost.CreateTransaction(SR.ToolStripItemPropertyChangeTransaction); + try + { + //Change the Text... + PropertyDescriptor textProp = TypeDescriptor.GetProperties(ToolStripItem)["Text"]; + string oldValue = (string)textProp.GetValue(ToolStripItem); + if (textProp != null && text != oldValue) + { + textProp.SetValue(ToolStripItem, text); + } + if (enterKeyPressed && selSvc != null) + { + SelectNextItem(selSvc, enterKeyPressed, designer); + } + } + catch (Exception e) + { + if (designerTransaction != null) + { + designerTransaction.Cancel(); + designerTransaction = null; + } + if (selMgr != null) + { + selMgr.Refresh(); + } + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + } + finally + { + if (designerTransaction != null) + { + designerTransaction.Commit(); + designerTransaction = null; + } + } + } + //Reset the DummyItem flag + dummyItemAdded = false; + } + else + { + // Refresh on SelectionManager... To Change Glyph Size. + if (dummyItemAdded) + { + dummyItemAdded = false; + RemoveItem(); + + if (designer.NewItemTransaction != null) + { + designer.NewItemTransaction.Cancel(); + designer.NewItemTransaction = null; + } + } + } + immediateParent.ResumeLayout(); + if (newItem != null && !newItem.IsOnDropDown) + { + if (newItem is ToolStripDropDownItem dropDown) + { + ToolStripItemDesigner itemDesigner = (ToolStripItemDesigner)designerHost.GetDesigner(newItem); + Rectangle itemBounds = itemDesigner.GetGlyphBounds(); + if (designerHost.RootComponent is Control parent) + { + if (bSvc != null) + { + Rectangle parentBounds = bSvc.ControlRectInAdornerWindow(parent); + if (!ToolStripDesigner.IsGlyphTotallyVisible(itemBounds, parentBounds)) + { + dropDown.HideDropDown(); + } + } + } + } + } + + // used the SelectionManager to Add the glyphs. + if (selMgr != null) + { + selMgr.Refresh(); + } + } + + /// + /// Disposes of this designer. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + //clean up + if (_editorNode != null) + { + _editorNode.CloseEditor(); + _editorNode = null; + } + + if (ToolStripItem != null) + { + ToolStripItem.Paint -= new System.Windows.Forms.PaintEventHandler(OnItemPaint); + } + // Now, unhook the component rename event + IComponentChangeService cs = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (cs != null) + { + cs.ComponentRename -= new ComponentRenameEventHandler(OnComponentRename); + } + + //clean up + if (selSvc != null) + { + selSvc.SelectionChanged -= new EventHandler(OnSelectionChanged); + } + //clean up the ToolStripItem Glyph if Any + if (bodyGlyph != null) + { + ToolStripAdornerWindowService toolStripAdornerWindowService = (ToolStripAdornerWindowService)GetService(typeof(ToolStripAdornerWindowService)); + if (toolStripAdornerWindowService != null && toolStripAdornerWindowService.DropDownAdorner.Glyphs.Contains(bodyGlyph)) + { + toolStripAdornerWindowService.DropDownAdorner.Glyphs.Remove(bodyGlyph); + } + } + // Remove the Collection + if (toolStripItemCustomMenuItemCollection != null && toolStripItemCustomMenuItemCollection.Count > 0) + { + foreach (ToolStripItem item in toolStripItemCustomMenuItemCollection) + { + item.Dispose(); + } + toolStripItemCustomMenuItemCollection.Clear(); + } + toolStripItemCustomMenuItemCollection = null; + } + base.Dispose(disposing); + } + + /// + /// Returns the owner of the current ToolStripItem. + /// + protected virtual Component GetOwnerForActionList() => (ToolStripItem.Placement == ToolStripItemPlacement.Main) ? ToolStripItem.GetCurrentParent() : ToolStripItem.Owner; + + internal virtual ToolStrip GetMainToolStrip() => ToolStripItem.Owner; + + public Rectangle GetGlyphBounds() + { + BehaviorService b = (BehaviorService)GetService(typeof(BehaviorService)); + Rectangle r = Rectangle.Empty; + if (b != null && ImmediateParent != null) + { + Point loc = b.ControlToAdornerWindow((Control)ImmediateParent); + r = ToolStripItem.Bounds; + r.Offset(loc); + } + return r; + } + + // Need to Fire ComponentChanging on all the DropDownItems. Please see "MorphToolStripItem" function for more details. + private void FireComponentChanging(ToolStripDropDownItem parent) + { + if (parent != null) + { + IComponentChangeService changeSvc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (changeSvc != null && parent.Site != null) + { + changeSvc.OnComponentChanging(parent, TypeDescriptor.GetProperties(parent)["DropDownItems"]); + } + foreach (ToolStripItem item in parent.DropDownItems) + { + //Dont Serialize the DesignerToolStripControlHost... + if (item is ToolStripDropDownItem dropDownItem && dropDownItem.DropDownItems.Count > 1 /*including TN*/) + { + FireComponentChanging(dropDownItem); + } + } + } + } + + private void FireComponentChanged(ToolStripDropDownItem parent) + { + if (parent != null) + { + IComponentChangeService changeSvc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (changeSvc != null && parent.Site != null) + { + changeSvc.OnComponentChanged(parent, TypeDescriptor.GetProperties(parent)["DropDownItems"], null, null); + } + + foreach (ToolStripItem item in parent.DropDownItems) + { + //Dont Serialize the DesignerToolStripControlHost... + if (item is ToolStripDropDownItem dropDownItem && dropDownItem.DropDownItems.Count > 1 /*including TN*/) + { + FireComponentChanged(dropDownItem); + } + } + } + } + + public void GetGlyphs(ref GlyphCollection glyphs, System.Windows.Forms.Design.Behavior.Behavior standardBehavior) + { + if (ImmediateParent != null) + { + Rectangle r = GetGlyphBounds(); + ToolStripDesignerUtils.GetAdjustedBounds(ToolStripItem, ref r); + BehaviorService b = (BehaviorService)GetService(typeof(BehaviorService)); + Rectangle parentBounds = b.ControlRectInAdornerWindow((Control)ImmediateParent); + if (parentBounds.Contains(r.Left, r.Top)) + { + // Dont paint the glyphs if we are opening a DropDown... + if (ToolStripItem.IsOnDropDown) + { + ToolStrip parent = ToolStripItem.GetCurrentParent(); + if (parent == null) + { + parent = ToolStripItem.Owner; + } + if (parent != null && parent.Visible) + { + glyphs.Add(new MiniLockedBorderGlyph(r, SelectionBorderGlyphType.Top, standardBehavior, true)); + glyphs.Add(new MiniLockedBorderGlyph(r, SelectionBorderGlyphType.Bottom, standardBehavior, true)); + glyphs.Add(new MiniLockedBorderGlyph(r, SelectionBorderGlyphType.Left, standardBehavior, true)); + glyphs.Add(new MiniLockedBorderGlyph(r, SelectionBorderGlyphType.Right, standardBehavior, true)); + } + } + else + { + glyphs.Add(new MiniLockedBorderGlyph(r, SelectionBorderGlyphType.Top, standardBehavior, true)); + glyphs.Add(new MiniLockedBorderGlyph(r, SelectionBorderGlyphType.Bottom, standardBehavior, true)); + glyphs.Add(new MiniLockedBorderGlyph(r, SelectionBorderGlyphType.Left, standardBehavior, true)); + glyphs.Add(new MiniLockedBorderGlyph(r, SelectionBorderGlyphType.Right, standardBehavior, true)); + } + } + } + } + + /// + /// Returns the root dropdown in the chain. + /// + internal ToolStripDropDown GetFirstDropDown(ToolStripItem currentItem) + { + if (currentItem.Owner is ToolStripDropDown) + { + ToolStripDropDown topmost = currentItem.Owner as ToolStripDropDown; + // walk back up the chain of windows to get the topmost + while (topmost.OwnerItem != null && (topmost.OwnerItem.Owner is ToolStripDropDown)) + { + topmost = topmost.OwnerItem.Owner as ToolStripDropDown; + } + return topmost; + } + return null; + } + + /// + /// This helper function resets the AutoSize property so that the item SNAPS back to its "preferredSize". + /// + private void HideDummyNode() + { + ToolStripItem.AutoSize = AutoSize; + if (_editorNode != null) + { + _editorNode.CloseEditor(); + _editorNode = null; + } + } + + /// + /// Get the designer set up to run. + /// + public override void Initialize(IComponent component) + { + base.Initialize(component); + //Shadow AutoSize + AutoSize = ToolStripItem.AutoSize; + Visible = true; + currentVisible = Visible; + //Shadow the AccessibleName as we are going to change it at DesignTime + AccessibleName = ToolStripItem.AccessibleName; + ToolStripItem.Paint += new System.Windows.Forms.PaintEventHandler(OnItemPaint); + //Change the AccessibleName to point to ToolStirpItem.Name + ToolStripItem.AccessibleName = ToolStripItem.Name; + // Now, hook the component rename event so we can update the AccessibleName + IComponentChangeService cs = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (cs != null) + { + cs.ComponentRename += new ComponentRenameEventHandler(this.OnComponentRename); + } + + //hook our SelectionService. + selSvc = (ISelectionService)GetService(typeof(ISelectionService)); + if (selSvc != null) + { + selSvc.SelectionChanged += new EventHandler(this.OnSelectionChanged); + } + } + + /// + /// Overriden to always Initialise the ToolStripItem with Text property. + /// + public override void InitializeNewComponent(IDictionary defaultValues) + { + //Set the Text only if the item is not created internally (via InSitu Edit) + if (!internalCreate) + { + ISite site = Component.Site; + if (site != null && Component is ToolStripDropDownItem) + { + if (defaultValues == null) + defaultValues = new Hashtable(); + defaultValues["Text"] = site.Name; + IComponent component = Component; + PropertyDescriptor pd = TypeDescriptor.GetProperties(ToolStripItem)["Text"]; + + if (pd != null && pd.PropertyType.Equals(typeof(string))) + { + string current = (string)pd.GetValue(component); + if (current == null || current.Length == 0) + { + pd.SetValue(component, site.Name); + } + } + } + } + base.InitializeNewComponent(defaultValues); + // ComboBoxes and TextBoxes shouldnt have Texts... In TextBoxBaseDesigner we do similar thing where we call the base (which sets the text) and then reset it back + if (Component is ToolStripTextBox || Component is ToolStripComboBox) + { + PropertyDescriptor textProp = TypeDescriptor.GetProperties(Component)["Text"]; + if (textProp != null && textProp.PropertyType == typeof(string) && !textProp.IsReadOnly && textProp.IsBrowsable) + { + textProp.SetValue(Component, ""); + } + } + } + + /// + /// This will morph the current item to the provided type "t" of the item... + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + internal virtual ToolStripItem MorphCurrentItem(Type t) + { + ToolStripItem newItem = null; + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host == null) + { + Debug.Fail("Couldn't get designer host!"); + return newItem; + } + + //create our transaction + DesignerTransaction designerTransaction = host.CreateTransaction(SR.ToolStripMorphingItemTransaction); + ToolStrip parent = (ToolStrip)ImmediateParent; + // Sepcial case overflow... + if (parent is ToolStripOverflow) + { + parent = ToolStripItem.Owner; + } + ToolStripMenuItemDesigner ownerItemDesigner = null; + + int dummyIndex = parent.Items.IndexOf(ToolStripItem); + string name = ToolStripItem.Name; + ToolStripItem ownerItem = null; + + // Get the main ToolStrip to Set the Glyph for the new Item once it is MORPHED. + if (ToolStripItem.IsOnDropDown) + { + if (ImmediateParent is ToolStripDropDown parentDropDown) + { + ownerItem = parentDropDown.OwnerItem; + if (ownerItem != null) + { + ownerItemDesigner = (ToolStripMenuItemDesigner)host.GetDesigner(ownerItem); + } + } + } + + try + { + //turn off Adding and Added Transactions.. + ToolStripDesigner.s_autoAddNewItems = false; + ComponentSerializationService _serializationService = GetService(typeof(ComponentSerializationService)) as ComponentSerializationService; + SerializationStore _serializedData = null; + if (_serializationService != null) + { + _serializedData = _serializationService.CreateStore(); + _serializationService.Serialize(_serializedData, Component); //notice the use of component... since we want to preserve the type. + + //Serialize all the DropDownItems for this Item.... + SerializationStore _serializedDataForDropDownItems = null; + ToolStripDropDownItem dropDownItem = ToolStripItem as ToolStripDropDownItem; + if (dropDownItem != null && typeof(ToolStripDropDownItem).IsAssignableFrom(t)) + { + // Hide the DropDown. + dropDownItem.HideDropDown(); + _serializedDataForDropDownItems = _serializationService.CreateStore(); + SerializeDropDownItems(dropDownItem, ref _serializedDataForDropDownItems, _serializationService); + //close the SerializationStore to Serialize Items.. + _serializedDataForDropDownItems.Close(); + } + + //close the SerializationStore to Serialize the ToolStripItem + _serializedData.Close(); + //Remove the currentItem that is getting morphed.. + IComponentChangeService changeSvc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (changeSvc != null) + { + if (parent.Site != null) + { + changeSvc.OnComponentChanging(parent, TypeDescriptor.GetProperties(parent)["Items"]); + } + else if (ownerItem != null) + { + changeSvc.OnComponentChanging(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"]); + changeSvc.OnComponentChanged(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"], null, null); + } + } + + FireComponentChanging(dropDownItem); + parent.Items.Remove(ToolStripItem); + host.DestroyComponent(ToolStripItem); + //Create our new Item + ToolStripItem component = (ToolStripItem)host.CreateComponent(t, name); + //Since destroying the original item took away its DropDownItems. We need to Deserialize the items again... + if (component is ToolStripDropDownItem) + { + if (_serializedDataForDropDownItems != null) + { + _serializationService.Deserialize(_serializedDataForDropDownItems); + } + } + + //Now deserialize the newItem to morph to the old item... + _serializationService.DeserializeTo(_serializedData, host.Container, false, true); + // Add the new Item... + newItem = (ToolStripItem)host.Container.Components[name]; + //Set the Image property and DisplayStyle... + if (newItem.Image == null && newItem is ToolStripButton) + { + Image image = null; + try + { + image = new Bitmap(typeof(ToolStripButton), "blank.bmp"); + } + catch (Exception ex) + { + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + + PropertyDescriptor imageProperty = TypeDescriptor.GetProperties(newItem)["Image"]; + Debug.Assert(imageProperty != null, "Could not find 'Image' property in ToolStripItem."); + if (imageProperty != null && image != null) + { + imageProperty.SetValue(newItem, image); + } + + PropertyDescriptor dispProperty = TypeDescriptor.GetProperties(newItem)["DisplayStyle"]; + Debug.Assert(dispProperty != null, "Could not find 'DisplayStyle' property in ToolStripItem."); + if (dispProperty != null) + { + dispProperty.SetValue(newItem, ToolStripItemDisplayStyle.Image); + } + + PropertyDescriptor imageTransProperty = TypeDescriptor.GetProperties(newItem)["ImageTransparentColor"]; + Debug.Assert(imageTransProperty != null, "Could not find 'DisplayStyle' property in ToolStripItem."); + if (imageTransProperty != null) + { + imageTransProperty.SetValue(newItem, Color.Magenta); + } + + } + + parent.Items.Insert(dummyIndex, newItem); + if (changeSvc != null) + { + if (parent.Site != null) + { + changeSvc.OnComponentChanged(parent, TypeDescriptor.GetProperties(parent)["Items"], null, null); + } + else if (ownerItem != null) + { + changeSvc.OnComponentChanging(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"]); + changeSvc.OnComponentChanged(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"], null, null); + } + } + + FireComponentChanged(dropDownItem); + // Add the Glyph for the DropDown ... We are responsible for the Glyh Addition since BodyGlyphs for DropDownItems are added by us. + if (newItem.IsOnDropDown && ownerItemDesigner != null) + { + ownerItemDesigner.RemoveItemBodyGlyph(newItem); + ownerItemDesigner.AddItemBodyGlyph(newItem); + } + // re start the ComponentAdding/Added events + ToolStripDesigner.s_autoAddNewItems = true; + //Invalidate the AdornerWindow to refresh selectionglyphs. + if (newItem != null) + { + if (newItem is ToolStripSeparator) + { + parent.PerformLayout(); + } + BehaviorService windowService = (BehaviorService)newItem.Site.GetService(typeof(BehaviorService)); + if (windowService != null) + { + windowService.Invalidate(); + } + + // set the selection to our new item.. since we destroyed Original component.. we have to ask SelectionServive from new Component + ISelectionService selSvc = (ISelectionService)newItem.Site.GetService(typeof(ISelectionService)); + if (selSvc != null) + { + selSvc.SetSelectedComponents(new object[] { newItem }, SelectionTypes.Replace); + } + } + } + } + catch + { + host.Container.Add(ToolStripItem); + parent.Items.Insert(dummyIndex, ToolStripItem); + if (designerTransaction != null) + { + designerTransaction.Cancel(); + designerTransaction = null; + } + } + finally + { + if (designerTransaction != null) + { + designerTransaction.Commit(); + designerTransaction = null; + } + } + return newItem; + } + + /// + /// Raised when a component's name changes. Here we update the AccessibleName Property to match the newName. + /// + private void OnComponentRename(object sender, ComponentRenameEventArgs e) + { + if (e.Component == ToolStripItem) + { + ToolStripItem.AccessibleName = e.NewName; + } + } + + /// + /// This can be used for OVERFLOW !!! + /// + private void OnItemPaint(object sender, System.Windows.Forms.PaintEventArgs e) + { + if (ToolStripItem.GetCurrentParent() is ToolStripDropDown dropDown) + { + if (selSvc != null) + { + if (!IsEditorActive && ToolStripItem.Equals(selSvc.PrimarySelection)) + { + BehaviorService behaviorService = (BehaviorService)GetService(typeof(BehaviorService)); + + if (behaviorService != null) + { + Point loc = behaviorService.ControlToAdornerWindow((Control)ImmediateParent); + Rectangle r = ToolStripItem.Bounds; + r.Offset(loc); + r.Inflate(GLYPHINSET, GLYPHINSET); + //this will allow any Glyphs to re-paint + //after this control and its designer has painted + behaviorService.ProcessPaintMessage(r); + + } + } + } + } + } + + /// + /// For ToolStripItems that are not MenuItems and are on Dropdown we need ot update Selection Rect. + /// + private void OnSelectionChanged(object sender, EventArgs e) + { + if (!(sender is ISelectionService sSvc)) + { + return; + } + //determine if we are selected + ToolStripItem currentSelection = sSvc.PrimarySelection as ToolStripItem; + // Accessibility information + if (ToolStripItem.AccessibilityObject is ToolStripItem.ToolStripItemAccessibleObject acc) + { + acc.AddState(AccessibleStates.None); + ToolStrip tool = GetMainToolStrip(); + if (sSvc.GetComponentSelected(ToolStripItem)) + { + ToolStrip owner = ImmediateParent as ToolStrip; + int focusIndex = 0; + if (owner != null) + { + focusIndex = owner.Items.IndexOf(currentSelection); + } + acc.AddState(AccessibleStates.Selected); + if (tool != null) + { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "MSAA: SelectionAdd, tool = " + tool.ToString()); + UnsafeNativeMethods.NotifyWinEvent((int)AccessibleEvents.SelectionAdd, new HandleRef(owner, owner.Handle), NativeMethods.OBJID_CLIENT, focusIndex + 1); + } + if (currentSelection == ToolStripItem) + { + acc.AddState(AccessibleStates.Focused); + if (tool != null) + { + UnsafeNativeMethods.NotifyWinEvent((int)AccessibleEvents.Focus, new HandleRef(owner, owner.Handle), NativeMethods.OBJID_CLIENT, focusIndex + 1); + } + } + } + } + + if (currentSelection != null && currentSelection.Equals(ToolStripItem) && !(ToolStripItem is ToolStripMenuItem)) + { + if (currentSelection.IsOnDropDown) + { + //If the Item is on DropDown ... Show its DropDown and all PArent Dropdown if not visible.. + IDesignerHost designerHost = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (designerHost != null) + { + if (currentSelection.Owner is ToolStripDropDown parentDropDown) + { + bool needRefresh = false; + if (parentDropDown.OwnerItem is ToolStripDropDownItem parentItem) + { + ToolStripMenuItemDesigner parentItemDesigner = (ToolStripMenuItemDesigner)designerHost.GetDesigner(parentItem); + if (parentItemDesigner != null) + { + parentItemDesigner.InitializeDropDown(); + } + needRefresh = true; + } + else if (parentDropDown is ContextMenuStrip) + { + // For ContextMenuStrip, we need use different ways to show the menu. + ToolStripDropDownDesigner parentDropDownDesigner = (ToolStripDropDownDesigner)designerHost.GetDesigner(parentDropDown); + if (parentDropDownDesigner != null) + { + parentDropDownDesigner.ShowMenu(currentSelection); + } + needRefresh = true; + } + + if (needRefresh) + { + // Refresh on SelectionManager... To Change Glyph Size. + SelectionManager selMgr = (SelectionManager)GetService(typeof(SelectionManager)); + // used the cached value... + if (selMgr != null) + { + selMgr.Refresh(); + } + // Invalidate the dropdown area. This is necessary when a different item is selected in the same dropdown. + BehaviorService behaviorService = (BehaviorService)GetService(typeof(BehaviorService)); + if (behaviorService != null) + { + behaviorService.Invalidate(parentDropDown.Bounds); + } + } + } + } + } + else if (currentSelection.Owner != null) + { + // The selected item could be in a MenuStrip, StatusStrip or ToolStrip. Need invalidate the BehaviorService to reflect the selection change. + BehaviorService behaviorService = (BehaviorService)GetService(typeof(BehaviorService)); + if (behaviorService != null) + { + behaviorService.Invalidate(behaviorService.ControlRectInAdornerWindow(currentSelection.Owner)); + } + } + } + } + + /// + /// Allows a designer to filter the set of properties the component it is designing will expose through the TypeDescriptor object. This method is called immediately before its corresponding "Post" method. If you are overriding this method you should call the base implementation before you perform your own filtering. + /// + protected override void PreFilterProperties(IDictionary properties) + { + base.PreFilterProperties(properties); + // Handle shadowed properties + string[] shadowProps = new string[] { "AutoSize", "AccessibleName", "Visible", "Overflow" }; + + PropertyDescriptor prop; + Attribute[] empty = new Attribute[0]; + for (int i = 0; i < shadowProps.Length; i++) + { + prop = (PropertyDescriptor)properties[shadowProps[i]]; + if (prop != null) + { + properties[shadowProps[i]] = TypeDescriptor.CreateProperty(typeof(ToolStripItemDesigner), prop, empty); + } + } + } + + // CALLED ONLY IF THE EDIT ACTION WAS ROLLBACKED!!! + public void RemoveItem() + { + dummyItemAdded = false; + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + if (host == null) + { + Debug.Fail("Couldn't get designer host!"); + return; + } + //Remove the dummy Item since the Edit was CANCELLED.. + ToolStrip parent = (ToolStrip)ImmediateParent; + if (parent is ToolStripOverflow) + { + parent = ParentComponent as ToolStrip; + } + parent.Items.Remove(ToolStripItem); + host.DestroyComponent(ToolStripItem); + } + + + // + // Resets the ToolStripItemAutoSize to be the default autosize + // + private void ResetAutoSize() => ShadowProperties["AutoSize"] = false; + + // + // Restores the AutoSize to be the value set in the property grid. + // + private void RestoreAutoSize() => ToolStripItem.AutoSize = (bool)ShadowProperties["AutoSize"]; + + // + // Resets the ToolStrip Visible to be the default value + // + private void ResetVisible() => Visible = true; + + // + // Restore Overflow + // + private void RestoreOverflow() => ToolStripItem.Overflow = (ToolStripItemOverflow)ShadowProperties["Overflow"]; + + // + // Resets Overflow + // + private void ResetOverflow() => ToolStripItem.Overflow = ToolStripItemOverflow.AsNeeded; + + // + // Resets the ToolStripItem AccessibleName to the default + // + private void ResetAccessibleName() => ShadowProperties["AccessibleName"] = null; + + // + // Restores the AutoSize to be the value set in the property grid. + // + private void RestoreAccessibleName() => ToolStripItem.AccessibleName = (string)ShadowProperties["AccessibleName"]; + + // internal method called to select the next item from the current item. + internal void SelectNextItem(ISelectionService service, bool enterKeyPressed, ToolStripDesigner designer) + { + if (ToolStripItem is ToolStripDropDownItem dropDownItem) + { + SetSelection(enterKeyPressed); + } + else + //We are here for simple ToolStripItems... + { + ToolStrip parent = (ToolStrip)ImmediateParent; + if (parent is ToolStripOverflow) + { + parent = ToolStripItem.Owner; + } + int currentIndex = parent.Items.IndexOf(ToolStripItem); + ToolStripItem nextItem = parent.Items[currentIndex + 1]; + // Set the Selection to the NEXT ITEM in the TOOLSTRIP... + ToolStripKeyboardHandlingService keyboardHandlingService = (ToolStripKeyboardHandlingService)GetService(typeof(ToolStripKeyboardHandlingService)); + if (keyboardHandlingService != null) + { + if (nextItem == designer.EditorNode) + { + keyboardHandlingService.SelectedDesignerControl = nextItem; + selSvc.SetSelectedComponents(null, SelectionTypes.Replace); + } + else + { + keyboardHandlingService.SelectedDesignerControl = null; + selSvc.SetSelectedComponents(new object[] { nextItem }); + } + } + } + } + + // Recursive function to add all the menuItems to the SerializationStore during Morphing.. + private void SerializeDropDownItems(ToolStripDropDownItem parent, ref SerializationStore _serializedDataForDropDownItems, ComponentSerializationService _serializationService) + { + foreach (ToolStripItem item in parent.DropDownItems) + { + //Dont Serialize the DesignerToolStripControlHost... + if (!(item is DesignerToolStripControlHost)) + { + _serializationService.Serialize(_serializedDataForDropDownItems, item); + if (item is ToolStripDropDownItem dropDownItem) + { + SerializeDropDownItems(dropDownItem, ref _serializedDataForDropDownItems, _serializationService); + } + } + } + } + + + // Sets the Item visibility to honor WYSIWYG + internal void SetItemVisible(bool toolStripSelected, ToolStripDesigner designer) + { + if (toolStripSelected) + { + // Set the Visiblity if different. + if (!currentVisible) + { + ToolStripItem.Visible = true; + if (designer != null && !designer.FireSyncSelection) + { + designer.FireSyncSelection = true; + } + } + } + else + { + if (!currentVisible) + { + ToolStripItem.Visible = currentVisible; + } + } + } + + private bool ShouldSerializeVisible() => !Visible; + + // + // Since we're shadowing autosize, we get called here to determine whether or not to serialize + // + private bool ShouldSerializeAutoSize() => (ShadowProperties.Contains("AutoSize")); + + // + // Since we're shadowing autosize, we get called here to determine whether or not to serialize + // + private bool ShouldSerializeAccessibleName() => (ShadowProperties["AccessibleName"] != null); + + // + // Since we're Overflow Size, we get called here to determine whether or not to serialize + // + private bool ShouldSerializeOverflow() => (ShadowProperties["Overflow"] != null); + + /// + /// This Function is called thru the ToolStripEditorManager which is listening for the F2 command. + /// + internal virtual void ShowEditNode(bool clicked) + { + // ACTIVATION ONLY FOR TOOLSTRIPMENUITEMS + if (ToolStripItem is ToolStripMenuItem) + { + if (_editorNode == null) + { + CreateDummyNode(); + } + + IDesignerHost designerHost = (IDesignerHost)Component.Site.GetService(typeof(IDesignerHost)); + ToolStrip parent = ImmediateParent as ToolStrip; + Debug.Assert(parent != null, "ImmediateParent is null for the current ToolStripItem !!"); + if (parent != null) + { + ToolStripDesigner parentDesigner = (ToolStripDesigner)designerHost.GetDesigner(parent); + BehaviorService b = (BehaviorService)GetService(typeof(BehaviorService)); + Point loc = b.ControlToAdornerWindow(parent); + + //Get the original ToolStripItem bounds. + Rectangle origBoundsInAdornerWindow = ToolStripItem.Bounds; + origBoundsInAdornerWindow.Offset(loc); + ToolStripItem.AutoSize = false; + _editorNode.SetWidth(ToolStripItem.Text); + if (parent.Orientation == Orientation.Horizontal) + { + ToolStripItem.Width = _editorNode.EditorToolStrip.Width + 2; + } + else + { + ToolStripItem.Height = _editorNode.EditorToolStrip.Height; + } + // Refresh the glyphs. + if (!dummyItemAdded) + { + b.SyncSelection(); + } + + if (ToolStripItem.Placement != ToolStripItemPlacement.None) + { + Rectangle boundsInAdornerWindow = ToolStripItem.Bounds; + boundsInAdornerWindow.Offset(loc); + + //Center it in verticaldirection. + if (parent.Orientation == Orientation.Horizontal) + { + boundsInAdornerWindow.X++; + boundsInAdornerWindow.Y += (ToolStripItem.Height - _editorNode.EditorToolStrip.Height) / 2; + boundsInAdornerWindow.Y++; + } + else + { + boundsInAdornerWindow.X += (ToolStripItem.Width - _editorNode.EditorToolStrip.Width) / 2; + boundsInAdornerWindow.X++; + } + _editorNode.Bounds = boundsInAdornerWindow; + //Invalidate the union of the original bounds and the new bounds. + boundsInAdornerWindow = Rectangle.Union(origBoundsInAdornerWindow, boundsInAdornerWindow); + b.Invalidate(boundsInAdornerWindow); + // PLEASE DONT CHANGE THIS ORDER !!! + if (parentDesigner != null && parentDesigner.EditManager != null) + { + parentDesigner.EditManager.ActivateEditor(ToolStripItem, clicked); + } + SelectionManager selMgr = (SelectionManager)GetService(typeof(SelectionManager)); + if (bodyGlyph != null) + { + selMgr.BodyGlyphAdorner.Glyphs.Remove(bodyGlyph); + } + } + else + { + ToolStripItem.AutoSize = AutoSize; + if (ToolStripItem is ToolStripDropDownItem) //We have no place to show this item... so Hide the DropDown + { + if (ToolStripItem is ToolStripDropDownItem ddItem) + { + ddItem.HideDropDown(); + } + // And select the parent... since we cannot show the current selection. + selSvc.SetSelectedComponents(new object[] { ImmediateParent }); + } + } + } + } + } + + // This method is called by the ToolStripDesigner to SetSelections to proper ToolStripItems after the parent ToolStripItem is committed. Consider this : the ToolStrip would cause the NEXT item on the TOPLEVEL to get selected... while on MenuStrip.. we would want the Child ToolStripItem in the DropDown to get selected after the TopLevel MenuStripItem is commited. + internal virtual bool SetSelection(bool enterKeyPressed) => false; + + internal override void ShowContextMenu(int x, int y) + { + ToolStripKeyboardHandlingService keySvc = (ToolStripKeyboardHandlingService)GetService(typeof(ToolStripKeyboardHandlingService)); + if (keySvc != null) + { + if (!keySvc.ContextMenuShownByKeyBoard) + { + BehaviorService b = (BehaviorService)GetService(typeof(BehaviorService)); + Point newPoint = Point.Empty; + if (b != null) + { + newPoint = b.ScreenToAdornerWindow(new Point(x, y)); + } + Rectangle itemBounds = GetGlyphBounds(); + if (itemBounds.Contains(newPoint)) + { + DesignerContextMenu.Show(x, y); + } + } + else + { + keySvc.ContextMenuShownByKeyBoard = false; + DesignerContextMenu.Show(x, y); + } + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemGlyph.cs new file mode 100644 index 00000000000..339791195f7 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemGlyph.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// The glyph we put over the items. Basically this sets the hit-testable area of the item itself. + /// + internal class ToolStripItemGlyph : ControlBodyGlyph + { + private readonly ToolStripItem _item; + private Rectangle _bounds; + private readonly ToolStripItemDesigner _itemDesigner; + + public ToolStripItemGlyph(ToolStripItem item, ToolStripItemDesigner itemDesigner, Rectangle bounds, System.Windows.Forms.Design.Behavior.Behavior b) : base(bounds, Cursors.Default, item, b) + { + _item = item; + _bounds = bounds; + _itemDesigner = itemDesigner; + } + + public ToolStripItem Item + { + get => _item; + } + + public override Rectangle Bounds + { + get => _bounds; + } + + public ToolStripItemDesigner ItemDesigner + { + get => _itemDesigner; + } + + /// + /// Abstract method that forces Glyph implementations to provide hit test logic. Given any point - if the Glyph has decided to be involved with that location, the Glyph will need to return a valid Cursor. Otherwise, returning null will cause the the BehaviorService to simply ignore it. + /// + public override Cursor GetHitTest(Point p) + { + if (_item.Visible && _bounds.Contains(p)) + { + return Cursors.Default; + } + return null; + } + + /// + /// Control host dont draw on Invalidation... + /// + public override void Paint(PaintEventArgs pe) + { + if (_item is ToolStripControlHost && _item.IsOnDropDown) + { + if (_item is System.Windows.Forms.ToolStripComboBox && VisualStyles.VisualStyleRenderer.IsSupported) + { + // When processing WM_PAINT and the OS has a theme enabled, the native ComboBox sends a WM_PAINT message to its parent when a theme is enabled in the OS forcing a repaint in the AdornerWindow generating an infinite WM_PAINT message processing loop. We guard against this here. + return; + } + _item.Invalidate(); + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs new file mode 100644 index 00000000000..f8694fcab1e --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs @@ -0,0 +1,2132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + internal class ToolStripKeyboardHandlingService + { + private ISelectionService _selectionService; + private IComponentChangeService _componentChangeSvc; + private IServiceProvider _provider; + private IMenuCommandService _menuCommandService; + private readonly IDesignerHost _designerHost; + //primary selection during shift operation is the LAST selected item which is different from selSvc.PrimarySelection, hence cache it + private object _shiftPrimary = null; + private bool _shiftPressed = false; + // our cache of currently selected DesignerToolStripControl Host.... + private object _currentSelection; + //is the templateNode in Insitu Mode? + private bool _templateNodeActive = false; + private ToolStripTemplateNode _activeTemplateNode = null; + //is the TemplateNode ContextMenu open. When the TemplateNode AddItems ContextMenu is opened we want to Disable all the Commands... And we enable them when the contextMenu closes... But if the menu closes by "enter Key" we get OnKeyDefault and hence go into InSitu Edit Mode.. to avoid this we have a new flag to IGNORE the first OnKeyDefault. + private bool _templateNodeContextMenuOpen = false; + // old commands + private ArrayList _oldCommands; + // our commands + private ArrayList _newCommands; + // need to add this separately since the VbDATA guys return us their paste command when the DataSource is copy pasted. + private MenuCommand _oldCommandPaste; + private MenuCommand _newCommandPaste; + private bool _commandsAdded = false; + private bool _copyInProgress = false; + private bool _cutOrDeleteInProgress = false; + private bool _contextMenuShownByKeyBoard = false; //We should know when the contextMenu is shown by KeyBoard shortcut. + private object _ownerItemAfterCut = null; // This value is set only of the ToolStripMenuItem is cut and now we need to reopen the dropDown which was closed in the previous CUT operation. + + /// + /// This creates a service for handling the keyboard navigation at desgin time. + /// + public ToolStripKeyboardHandlingService(IServiceProvider serviceProvider) + { + _provider = serviceProvider; + _selectionService = (ISelectionService)serviceProvider.GetService(typeof(ISelectionService)); + Debug.Assert(_selectionService != null, "ToolStripKeyboardHandlingService relies on the selection service, which is unavailable."); + if (_selectionService != null) + { + _selectionService.SelectionChanging += new EventHandler(OnSelectionChanging); + _selectionService.SelectionChanged += new EventHandler(OnSelectionChanged); + } + + _designerHost = (IDesignerHost)_provider.GetService(typeof(IDesignerHost)); + Debug.Assert(_designerHost != null, "ToolStripKeyboardHandlingService relies on the selection service, which is unavailable."); + if (_designerHost != null) + { + _designerHost.AddService(typeof(ToolStripKeyboardHandlingService), this); + } + _componentChangeSvc = (IComponentChangeService)_designerHost.GetService(typeof(IComponentChangeService)); + Debug.Assert(_componentChangeSvc != null, "ToolStripKeyboardHandlingService relies on the componentChange service, which is unavailable."); + if (_componentChangeSvc != null) + { + _componentChangeSvc.ComponentRemoved += new ComponentEventHandler(OnComponentRemoved); + } + } + + //Currently active TemplateNode + internal ToolStripTemplateNode ActiveTemplateNode + { + get => _activeTemplateNode; + set + { + _activeTemplateNode = value; + ResetActiveTemplateNodeSelectionState(); + } + } + + // This property is set on the controlDesigner and used in the ToolStripItemDesigner. There is no way of knowing whether the ContextMenu is show via-keyBoard or Click and we need to know this since we check if the Bounds are within the toolStripItem while showing the ContextMenu. + internal bool ContextMenuShownByKeyBoard + { + get => _contextMenuShownByKeyBoard; + set => _contextMenuShownByKeyBoard = value; + } + + // When Copy (Through Control + Drag) this boolean is set to true. Problem is that during copy the DesignerUtils creates new components and as a result the ToolStripMenuItemDesigner and ToolStripDesigners get the "ComponentAdding/ComponentAdded" events where they try to parent the components. We dont need to "parent" in case of control + drag. + internal bool CopyInProgress + { + get => _copyInProgress; + set + { + if (value != CopyInProgress) + { + _copyInProgress = value; + } + } + } + + + // We need to listen to MenuCommands.Delete since we are going to change the selection here instead of OnComponentRemoved The OnComponentRemoved gets called through various different places like Partial Reload, Full Reload and Undo-Redo transactions Changing the selection in "OnComponentRemoved" thus is expensive in terms of flicker and code that gets run causing PERF hit. + internal bool CutOrDeleteInProgress + { + get => _cutOrDeleteInProgress; + set + { + if (value != _cutOrDeleteInProgress) + { + _cutOrDeleteInProgress = value; + } + } + } + + /// + /// Retrieves the selection service, which tthis service uses while selecting the toolStrip Item. + /// + private IDesignerHost Host + { + get => _designerHost; + } + + /// + /// Retrieves the menu editor service, which we cache for speed. + /// + private IMenuCommandService MenuService + { + get + { + if (_menuCommandService == null) + { + if (_provider != null) + { + _menuCommandService = (IMenuCommandService)_provider.GetService(typeof(IMenuCommandService)); + } + } + return _menuCommandService; + } + } + + // When the TemplateNode gets selected ... we dont set in the SelectionService.SelectedComponents since we want to blank out the propertygrid ... so we keep the selected cache here. + internal object SelectedDesignerControl + { + get => _currentSelection; + set + { + if (value != SelectedDesignerControl) + { + if (SelectedDesignerControl is DesignerToolStripControlHost prevDesignerNode) + { + prevDesignerNode.RefreshSelectionGlyph(); + } + _currentSelection = value; + if (_currentSelection != null) + { + if (_currentSelection is DesignerToolStripControlHost curDesignerNode) + { + curDesignerNode.SelectControl(); + if (curDesignerNode.AccessibilityObject is ToolStripItem.ToolStripItemAccessibleObject acc) + { + acc.AddState(AccessibleStates.Selected | AccessibleStates.Focused); + ToolStrip owner = curDesignerNode.GetCurrentParent() as ToolStrip; + int focusIndex = 0; + if (owner != null) + { + focusIndex = owner.Items.IndexOf(curDesignerNode); + } + UnsafeNativeMethods.NotifyWinEvent((int)AccessibleEvents.SelectionAdd, new HandleRef(owner, owner.Handle), NativeMethods.OBJID_CLIENT, focusIndex + 1); + UnsafeNativeMethods.NotifyWinEvent((int)AccessibleEvents.Focus, new HandleRef(owner, owner.Handle), NativeMethods.OBJID_CLIENT, focusIndex + 1); + } + } + } + } + } + } + + internal object OwnerItemAfterCut + { + get => _ownerItemAfterCut; + set => _ownerItemAfterCut = value; + } + + + // When shift key is pressed we need to know where to start from .. this object keeps a track of that item. + internal object ShiftPrimaryItem + { + get => _shiftPrimary; + set => _shiftPrimary = value; + } + + /// + /// Retrieves the selection service, which tthis service uses while selecting the toolStrip Item. + /// + private ISelectionService SelectionService + { + get => _selectionService; + } + + // When the ToolStripTemplateNode becomes active, the ToolStripKeyBoardHandlingService shouldnt process any MenuCommands... + internal bool TemplateNodeActive + { + get => _templateNodeActive; + set + { + _templateNodeActive = value; + + //Disable all our Commands when TemplateNode is Active. Remove the new Commands + if (_newCommands != null) + { + foreach (MenuCommand newCommand in _newCommands) + { + newCommand.Enabled = !_templateNodeActive; + } + } + } + } + + // boolean which returns if the TemplateNode contextMenu is open. + internal bool TemplateNodeContextMenuOpen + { + get => _templateNodeContextMenuOpen; + set + { + _templateNodeContextMenuOpen = value; + //Disable all our Commands when templateNodeContextMenuOpen. Remove the new Commands + if (_newCommands != null) + { + foreach (MenuCommand newCommand in _newCommands) + { + newCommand.Enabled = !_templateNodeActive; + } + } + } + } + + // Adds our commands to the MenuCommandService. + public void AddCommands() + { + IMenuCommandService mcs = MenuService; + if (mcs != null & !_commandsAdded) + { + // Demand Create the oldCommands + if (_oldCommands == null) + { + PopulateOldCommands(); + } + //Remove the Old Commands + foreach (MenuCommand oldCommand in _oldCommands) + { + if (oldCommand != null) + { + mcs.RemoveCommand(oldCommand); + } + } + // DemandCreate the new Commands. + if (_newCommands == null) + { + PopulateNewCommands(); + } + // Add our Commands + foreach (MenuCommand newCommand in _newCommands) + { + if (newCommand != null && mcs.FindCommand(newCommand.CommandID) == null) + { + mcs.AddCommand(newCommand); + } + } + _commandsAdded = true; + } + } + + // private function to get Next toolStripItem based on direction. + private ToolStripItem GetNextItem(ToolStrip parent, ToolStripItem startItem, ArrowDirection direction) + { + if (parent.RightToLeft == RightToLeft.Yes && (direction == ArrowDirection.Left || direction == ArrowDirection.Right)) + { + if (direction == ArrowDirection.Right) + { + direction = ArrowDirection.Left; + } + else if (direction == ArrowDirection.Left) + { + direction = ArrowDirection.Right; + } + } + return parent.GetNextItem(startItem, direction); + } + + /// + /// This is the private helper function which gets the next control in the TabOrder.. + /// + private Control GetNextControlInTab(Control basectl, Control ctl, bool forward) + { + if (forward) + { + while (ctl != basectl) + { + int targetIndex = ctl.TabIndex; + bool hitCtl = false; + Control found = null; + Control p = ctl.Parent; + // Cycle through the controls in z-order looking for the one with the next highest tab index. Because there can be dups, we have to start with the existing tab index and remember to exclude the current control. + int parentControlCount = 0; + Control.ControlCollection parentControls = (Control.ControlCollection)p.Controls; + if (parentControls != null) + { + parentControlCount = parentControls.Count; + } + + for (int c = 0; c < parentControlCount; c++) + { + // The logic for this is a bit lengthy, so I have broken it into separate caluses: We are not interested in ourself. + if (parentControls[c] != ctl) + { + // We are interested in controls with >= tab indexes to ctl. We must include those controls with equal indexes to account for duplicate indexes. + if (parentControls[c].TabIndex >= targetIndex) + { + // Check to see if this control replaces the "best match" we've already found. + if (found == null || found.TabIndex > parentControls[c].TabIndex) + { + // Finally, check to make sure that if this tab index is the same as ctl, that we've already encountered ctl in the z-order. If it isn't the same, than we're more than happy with it. + if (parentControls[c].Site != null && parentControls[c].TabIndex != targetIndex || hitCtl) + { + found = parentControls[c]; + } + } + } + } + else + { + // We track when we have encountered "ctl". We never want to select ctl again, but we want to know when we've seen it in case we find another control with the same tab index. + hitCtl = true; + } + } + + if (found != null) + { + return found; + } + + ctl = ctl.Parent; + } + } + else + { + if (ctl != basectl) + { + int targetIndex = ctl.TabIndex; + bool hitCtl = false; + Control found = null; + Control p = ctl.Parent; + // Cycle through the controls in reverse z-order looking for the next lowest tab index. We must start with the same tab index as ctl, because there can be dups. + int parentControlCount = 0; + Control.ControlCollection parentControls = (Control.ControlCollection)p.Controls; + if (parentControls != null) + { + parentControlCount = parentControls.Count; + } + + for (int c = parentControlCount - 1; c >= 0; c--) + { + // The logic for this is a bit lengthy, so I have broken it into separate caluses: We are not interested in ourself. + if (parentControls[c] != ctl) + { + // We are interested in controls with <= tab indexes to ctl. We must include those controls with equal indexes to account for duplicate indexes. + if (parentControls[c].TabIndex <= targetIndex) + { + // Check to see if this control replaces the "best match" we've already found. + if (found == null || found.TabIndex < parentControls[c].TabIndex) + { + // Finally, check to make sure that if this tab index is the same as ctl, that we've already encountered ctl in the z-order. If it isn't the same, than we're more than happy with it. + if (parentControls[c].TabIndex != targetIndex || hitCtl) + { + found = parentControls[c]; + } + } + } + } + else + { + // We track when we have encountered "ctl". We never want to select ctl again, but we want to know when we've seen it in case we find another control with the same tab index. + hitCtl = true; + } + } + + // If we were unable to find a control we should return the control's parent. However, if that parent is us, return NULL. + if (found != null) + { + ctl = found; + } + else + { + if (p == basectl) + { + return null; + } + else + { + return p; + } + } + } + } + return ctl == basectl ? null : ctl; + } + + // this will invoke the OLD command from our command handler. + private void InvokeOldCommand(object sender) + { + MenuCommand command = sender as MenuCommand; + foreach (MenuCommand oldCommand in _oldCommands) + { + if (oldCommand != null && oldCommand.CommandID == command.CommandID) + { + oldCommand.Invoke(); + break; + } + } + } + + private void OnComponentRemoved(object sender, ComponentEventArgs e) + { + bool toolStripPresent = false; + ComponentCollection comps = _designerHost.Container.Components; + foreach (IComponent comp in comps) + { + if (comp is ToolStrip) + { + toolStripPresent = true; + break; + } + } + if (!toolStripPresent) + { + ToolStripKeyboardHandlingService keyboardHandlingService = (ToolStripKeyboardHandlingService)_provider.GetService(typeof(ToolStripKeyboardHandlingService)); + if (keyboardHandlingService != null) + { + //since we are going away .. restore the old commands. + keyboardHandlingService.RestoreCommands(); + // clean up. + keyboardHandlingService.RemoveCommands(); + _designerHost.RemoveService(typeof(ToolStripKeyboardHandlingService)); + } + } + } + + public bool OnContextMenu(int x, int y) + { + if (TemplateNodeActive) + { + return true; + } + // commandsAdded means that either toolstrip, toolSripItem or templatenode is selected. + if (_commandsAdded && x == -1 && y == -1) + { + ContextMenuShownByKeyBoard = true; + Point p = Cursor.Position; + x = p.X; + y = p.Y; + } + + // This has to be done since ToolStripTemplateNode is unsited component that supports its own contextMenu. When the Selection is null, templateNode can be selected. So this block of code here checks if ToolStripKeyBoardHandlingService is present if so, tries to check if the templatenode is selected if so, then gets the templateNode and shows the ContextMenu. + if (!(SelectionService.PrimarySelection is Component selComp)) + { + if (SelectedDesignerControl is DesignerToolStripControlHost controlHost) + { + if (controlHost.Control is ToolStripTemplateNode.TransparentToolStrip tool) + { + ToolStripTemplateNode node = tool.TemplateNode; + if (node != null) + { + node.ShowContextMenu(new Point(x, y)); + return true; + } + } + } + } + return false; + } + + // Handler for Copy Command + private void OnCommandCopy(object sender, EventArgs e) + { + bool cutCommand = false; + try + { + //If the Command is CUT and the new Selection is DesignerToolStripControlHost then select it and open its parentDropDown. + if (sender is MenuCommand com && com.CommandID == StandardCommands.Cut) + { + cutCommand = true; + CutOrDeleteInProgress = true; + } + + // INVOKE THE OldCommand + InvokeOldCommand(sender); + // END + + if (cutCommand) + { + if (OwnerItemAfterCut is ToolStripDropDownItem parentItem) + { + ToolStripDropDown dropDown = parentItem.DropDown; + if (Host.GetDesigner(dropDown) is ToolStripDropDownDesigner dropDownDesigner) + { + SelectionService.SetSelectedComponents(new object[] { dropDownDesigner.Component }, SelectionTypes.Replace); + } + else if (parentItem != null && !(parentItem.DropDown.Visible)) + { + if (Host.GetDesigner(parentItem) is ToolStripMenuItemDesigner designer) + { + designer.SetSelection(true); + if (SelectedDesignerControl is DesignerToolStripControlHost curDesignerNode) + { + curDesignerNode.SelectControl(); + } + } + } + } + } + + // this is done So that the Data Behavior doesnt mess up with the copy command during addition of the ToolStrip.. + IMenuCommandService mcs = MenuService; + if (mcs != null) + { + if (_newCommandPaste == null) + { + _oldCommandPaste = mcs.FindCommand(StandardCommands.Paste); + if (_oldCommandPaste != null) + { + mcs.RemoveCommand(_oldCommandPaste); + } + _newCommandPaste = new MenuCommand(new EventHandler(OnCommandPaste), StandardCommands.Paste); + if (_newCommandPaste != null && mcs.FindCommand(_newCommandPaste.CommandID) == null) + { + mcs.AddCommand(_newCommandPaste); + } + } + } + } + finally + { + cutCommand = false; + CutOrDeleteInProgress = false; + } + } + + private void OnCommandDelete(object sender, EventArgs e) + { + try + { + CutOrDeleteInProgress = true; + // INVOKE THE OldCommand + InvokeOldCommand(sender); + // END + } + finally + { + CutOrDeleteInProgress = false; + } + + } + + // Handler for Paste Command + private void OnCommandPaste(object sender, EventArgs e) + { + //IF TemplateNode is Active DO NOT Support Paste. This is what MainMenu did + // We used to incorrectly paste the item to the parent's collection; so inorder to make a simple fix I am being consistent with MainMenu + if (TemplateNodeActive) + { + return; + } + ISelectionService selSvc = SelectionService; + IDesignerHost host = Host; + if (selSvc != null && host != null) + { + if (!(selSvc.PrimarySelection is IComponent comp)) + { + comp = (IComponent)SelectedDesignerControl; + } + ToolStripItem item = comp as ToolStripItem; + ToolStrip parent = null; + //Case 1: If SelectedObj is ToolStripItem select all items in its immediate parent. + if (item != null) + { + parent = item.GetCurrentParent() as ToolStrip; + } + if (parent != null) + { + parent.SuspendLayout(); + } + + // INVOKE THE OldCommand + if (_oldCommandPaste != null) + { + _oldCommandPaste.Invoke(); + } + + if (parent != null) + { + parent.ResumeLayout(); + // Since the Glyphs dont get correct bounds as the ToolStrip Layout is suspended .. force Glyph Updates. + BehaviorService behaviorService = (BehaviorService)_provider.GetService(typeof(BehaviorService)); + if (behaviorService != null) + { + behaviorService.SyncSelection(); + } + + // For ContextMenuStrip; since its not a control .. we dont get called on GetGlyphs directly through the BehaviorService So we need this internal call to push the glyphs on the SelectionManager + if (host.GetDesigner(item) is ToolStripItemDesigner designer) + { + ToolStripDropDown dropDown = designer.GetFirstDropDown(item); + if (dropDown != null && !dropDown.IsAutoGenerated) + { + if (host.GetDesigner(dropDown) is ToolStripDropDownDesigner dropDownDesigner) + { + dropDownDesigner.AddSelectionGlyphs(); + } + } + } + + // For Items on DropDown .. we have to manage Glyphs... + if (parent is ToolStripDropDown parentDropDown && parentDropDown.Visible) + { + if (parentDropDown.OwnerItem is ToolStripDropDownItem ownerItem) + { + if (host.GetDesigner(ownerItem) is ToolStripMenuItemDesigner itemDesigner) + { + itemDesigner.ResetGlyphs(ownerItem); + } + } + } + + // Get the Selection and ShowDropDown only on ToolStripDropDownItems to show dropDowns after paste operation. + if (selSvc.PrimarySelection is ToolStripDropDownItem dropDownItem && dropDownItem.DropDown.Visible) + { + //Hide the DropDown + dropDownItem.HideDropDown(); + if (host.GetDesigner(dropDownItem) is ToolStripMenuItemDesigner selectedItemDesigner) + { + selectedItemDesigner.InitializeDropDown(); + selectedItemDesigner.InitializeBodyGlyphsForItems(false, dropDownItem); + selectedItemDesigner.InitializeBodyGlyphsForItems(true, dropDownItem); + } + } + } + } + } + + // Handler for Home Command + private void OnCommandHome(object sender, EventArgs e) + { + ISelectionService selSvc = SelectionService; + if (selSvc != null) + { + if (!(selSvc.PrimarySelection is ToolStripItem item)) + { + item = SelectedDesignerControl as ToolStripItem; + } + // Process Keys only if we are a ToolStripItem and the TemplateNode is not in Insitu Mode. + if (item != null) + { + //only select the last item only if there is an Item added in addition to the TemplateNode... + ToolStrip parent = item.GetCurrentParent(); + int count = parent.Items.Count; + if (count >= 3) //3 //3 for the total number of items .. two ToolStripItems + 1 TemplateNode. + { + bool shiftPressed = (Control.ModifierKeys & Keys.Shift) > 0; + if (shiftPressed) + { + //Select all the items between current "item" till the Last item + int startIndexOfSelection = 0; + int endIndexOfSelection = Math.Max(0, parent.Items.IndexOf(item)); + int countofItemsSelected = (endIndexOfSelection - startIndexOfSelection) + 1; + + object[] totalObjects = new object[countofItemsSelected]; + int j = 0; + for (int i = startIndexOfSelection; i <= endIndexOfSelection; i++) + { + totalObjects[j++] = parent.Items[i]; + } + selSvc.SetSelectedComponents(totalObjects, SelectionTypes.Replace); + } + else + { + SetSelection(parent.Items[0]); + } + } + } + } + } + + // Handler for End Command + private void OnCommandEnd(object sender, EventArgs e) + { + ISelectionService selSvc = SelectionService; + if (selSvc != null) + { + if (!(selSvc.PrimarySelection is ToolStripItem item)) + { + item = SelectedDesignerControl as ToolStripItem; + } + // Process Keys only if we are a ToolStripItem and the TemplateNode is not in Insitu Mode. + if (item != null) + { + + //only select the last item only if there is an Item added in addition to the TemplateNode... + ToolStrip parent = item.GetCurrentParent(); + int count = parent.Items.Count; + if (count >= 3) //3 //3 for the total number of items .. two ToolStripItems + 1 TemplateNode. + { + bool shiftPressed = (Control.ModifierKeys & Keys.Shift) > 0; + if (shiftPressed) + { + //Select all the items between current "item" till the Last item + int startIndexOfSelection = parent.Items.IndexOf(item); + int endIndexOfSelection = Math.Max(startIndexOfSelection, count - 2); + int countofItemsSelected = (endIndexOfSelection - startIndexOfSelection) + 1; + + object[] totalObjects = new object[countofItemsSelected]; + int j = 0; + for (int i = startIndexOfSelection; i <= endIndexOfSelection; i++) + { + totalObjects[j++] = parent.Items[i]; + } + selSvc.SetSelectedComponents(totalObjects, SelectionTypes.Replace); + } + else + { + SetSelection(parent.Items[count - 2]); + } + } + } + } + } + + // Handler for SelectALL Command + private void OnCommandSelectAll(object sender, EventArgs e) + { + ISelectionService selSvc = SelectionService; + if (selSvc != null) + { + object selectedObj = selSvc.PrimarySelection; + //Case 1: If SelectedObj is ToolStripItem select all items in its immediate parent. + if (selectedObj is ToolStripItem) + { + ToolStripItem selectedItem = selectedObj as ToolStripItem; + ToolStrip parent = selectedItem.GetCurrentParent() as ToolStrip; + if (parent is ToolStripOverflow) + { + parent = selectedItem.Owner; + } + SelectItems(parent); + BehaviorService behaviorService = (BehaviorService)_provider.GetService(typeof(BehaviorService)); + if (behaviorService != null) + { + behaviorService.Invalidate(); + } + return; + } + // Case 2: if SelectedObj is ToolStrip ... then select all the item contained in it. + if (selectedObj is ToolStrip) + { + ToolStrip parent = selectedObj as ToolStrip; + SelectItems(parent); + return; + } + //Case 3: if selectedOj is ToolStripPanel ... select the ToolStrips within the ToolStripPanel... + if (selectedObj is ToolStripPanel) + { + ToolStripPanel parentToolStripPanel = selectedObj as ToolStripPanel; + selSvc.SetSelectedComponents((ICollection)parentToolStripPanel.Controls, SelectionTypes.Replace); + return; + } + } + } + + // this will get called for "Chrome Panel" command. We have to show the ItemList if The TemplateNode is selected. + private void OnKeyShowDesignerActions(object sender, EventArgs e) + { + ISelectionService selSvc = SelectionService; + if (selSvc != null) + { + if (selSvc.PrimarySelection == null) + { + if (SelectedDesignerControl is DesignerToolStripControlHost controlHost) + { + if (controlHost.Control is ToolStripTemplateNode.TransparentToolStrip tool) + { + ToolStripTemplateNode node = tool.TemplateNode; + if (node != null) + { + node.ShowDropDownMenu(); + return; + } + } + } + } + } + // INVOKE THE OldCommand + InvokeOldCommand(sender); + // END + } + + // Command handler for enter key. + private void OnKeyDefault(object sender, EventArgs e) + { + // Return if the contextMenu was open during this KeyDefault... + if (_templateNodeContextMenuOpen) + { + _templateNodeContextMenuOpen = false; + return; + } + + // Return key. Handle it like a double-click on the primary selection + ISelectionService selSvc = SelectionService; + IDesignerHost host = Host; + if (selSvc != null) + { + if (!(selSvc.PrimarySelection is IComponent pri)) + { + if (SelectedDesignerControl is DesignerToolStripControlHost typeHereNode) + { + if (host != null) + { + if (typeHereNode.IsOnDropDown && !typeHereNode.IsOnOverflow) + { + ToolStripDropDownItem ownerItem = (ToolStripDropDownItem)((ToolStripDropDown)(typeHereNode.Owner)).OwnerItem; + if (host.GetDesigner(ownerItem) is ToolStripMenuItemDesigner itemDesigner) + { + if (!itemDesigner.IsEditorActive) + { + itemDesigner.EditTemplateNode(true); + if (ActiveTemplateNode != null) + { + ActiveTemplateNode.ignoreFirstKeyUp = true; + } + } + } + } + else + { + if (host.GetDesigner(typeHereNode.Owner) is ToolStripDesigner tooldesigner) + { + tooldesigner.ShowEditNode(true); + if (ActiveTemplateNode != null) + { + ActiveTemplateNode.ignoreFirstKeyUp = true; + } + } + } + } + } + } + else + { + if (host != null) + { + IDesigner designer = host.GetDesigner(pri); + if (designer is ToolStripMenuItemDesigner tooldesigner) + { + if (tooldesigner.IsEditorActive) + { + return; + } + else + { + tooldesigner.ShowEditNode(false); + if (ActiveTemplateNode != null) + { + ActiveTemplateNode.ignoreFirstKeyUp = true; + } + } + } + else if (designer != null) + { + // INVOKE THE OldCommand + InvokeOldCommand(sender); + } + } + } + } + } + + /// + /// This is a function which gets called when the item goes into InSitu Edit mode. + /// + private void OnKeyEdit(object sender, EventArgs e) + { + // This method allows the ToolStrip Template Node into the EditMode. + ISelectionService selSvc = SelectionService; + IDesignerHost host = Host; + if (selSvc != null) + { + if (!(selSvc.PrimarySelection is IComponent comp)) + { + comp = (IComponent)SelectedDesignerControl; + } + + if (comp is ToolStripItem) + { + if (host != null) + { + CommandID cmd = ((MenuCommand)sender).CommandID; + if (cmd.Equals(MenuCommands.EditLabel)) + { + if (comp is ToolStripMenuItem) + { + if (host.GetDesigner(comp) is ToolStripMenuItemDesigner designer) + { + if (!designer.IsEditorActive) + { + designer.ShowEditNode(false); + } + } + } + if (comp is DesignerToolStripControlHost) + { + DesignerToolStripControlHost typeHereNode = comp as DesignerToolStripControlHost; + if (typeHereNode.IsOnDropDown) + { + ToolStripDropDownItem ownerItem = (ToolStripDropDownItem)((ToolStripDropDown)(typeHereNode.Owner)).OwnerItem; + if (host.GetDesigner(ownerItem) is ToolStripMenuItemDesigner itemDesigner) + { + if (!itemDesigner.IsEditorActive) + { + itemDesigner.EditTemplateNode(false); + } + } + } + else + { + if (host.GetDesigner(typeHereNode.Owner) is ToolStripDesigner tooldesigner) + { + tooldesigner.ShowEditNode(false); + } + } + } + } + } + } + } + } + + /// + /// This is a function which gets called when the arrow keys are used at design time on ToolStrips. + /// + private void OnKeyMove(object sender, EventArgs e) + { + // Arrow keys. Begin a drag if the selection isn't locked. + ISelectionService selSvc = SelectionService; + if (selSvc != null) + { + MenuCommand cmd = (MenuCommand)sender; + if (cmd.CommandID.Equals(MenuCommands.KeySizeWidthIncrease) || cmd.CommandID.Equals(MenuCommands.KeySizeWidthDecrease) || + cmd.CommandID.Equals(MenuCommands.KeySizeHeightDecrease) || cmd.CommandID.Equals(MenuCommands.KeySizeHeightIncrease)) + { + _shiftPressed = true; + } + else + { + _shiftPressed = false; + } + + // check for ContextMenu.. + if (selSvc.PrimarySelection is ContextMenuStrip contextStrip) + { + if (cmd.CommandID.Equals(MenuCommands.KeyMoveDown)) + { + ProcessUpDown(true); + } + return; + } + + if (!(selSvc.PrimarySelection is ToolStripItem item)) + { + item = SelectedDesignerControl as ToolStripItem; + } + + // Process Keys only if we are a ToolStripItem and the TemplateNode is not in Insitu Mode. + if (item != null) + { + if (cmd.CommandID.Equals(MenuCommands.KeyMoveRight) || cmd.CommandID.Equals(MenuCommands.KeyNudgeRight) || cmd.CommandID.Equals(MenuCommands.KeySizeWidthIncrease)) + { + if (!ProcessRightLeft(true)) + { + RotateTab(false); + return; + } + } + if (cmd.CommandID.Equals(MenuCommands.KeyMoveLeft) || cmd.CommandID.Equals(MenuCommands.KeyNudgeLeft) || cmd.CommandID.Equals(MenuCommands.KeySizeWidthDecrease)) + { + if (!ProcessRightLeft(false)) + { + RotateTab(true); + return; + } + } + if (cmd.CommandID.Equals(MenuCommands.KeyMoveDown) || cmd.CommandID.Equals(MenuCommands.KeyNudgeDown) || cmd.CommandID.Equals(MenuCommands.KeySizeHeightIncrease)) + { + ProcessUpDown(true); + return; + } + if (cmd.CommandID.Equals(MenuCommands.KeyMoveUp) || cmd.CommandID.Equals(MenuCommands.KeyNudgeUp) || cmd.CommandID.Equals(MenuCommands.KeySizeHeightDecrease)) + { + ProcessUpDown(false); + return; + } + } + else + { + // INVOKE THE OldCommand + InvokeOldCommand(sender); + } + } + } + + /// + /// This is a function which gets called when Cancel is pressed when we are on ToolStripItem. + /// + private void OnKeyCancel(object sender, EventArgs e) + { + ISelectionService selSvc = SelectionService; + if (selSvc != null) + { + if (!(selSvc.PrimarySelection is ToolStripItem item)) + { + item = SelectedDesignerControl as ToolStripItem; + } + // Process Keys only if we are a ToolStripItem and the TemplateNode is not in Insitu Mode. + if (item != null) + { + MenuCommand cmd = (MenuCommand)sender; + bool reverse = (cmd.CommandID.Equals(MenuCommands.KeyReverseCancel)); + RotateParent(reverse); + return; + } + else + { + // Check if the ToolStripDropDown (which is designable) is currently selected. If so this should select the "RootComponent" + if (selSvc.PrimarySelection is ToolStripDropDown dropDown && dropDown.Site != null) + { + selSvc.SetSelectedComponents(new object[] { Host.RootComponent }, SelectionTypes.Replace); + } + else + { + // INVOKE THE OldCommand + InvokeOldCommand(sender); + } + } + } + } + + /// + /// This function allows the CommandSet to select the right item when the Tab and Arrow keys are used. + /// + private void OnKeySelect(object sender, EventArgs e) + { + MenuCommand cmd = (MenuCommand)sender; + bool reverse = (cmd.CommandID.Equals(MenuCommands.KeySelectPrevious)); + ProcessKeySelect(reverse, cmd); + } + + /// + /// Called when the current selection changes. Here we determine what commands can and can't be enabled. + /// + private void OnSelectionChanging(object sender, EventArgs e) + { + if (!(SelectionService.PrimarySelection is Component primarySelection)) + { + primarySelection = SelectedDesignerControl as ToolStripItem; + } + + ToolStrip tool = primarySelection as ToolStrip; + if (tool != null) + { + InheritanceAttribute ia = (InheritanceAttribute)TypeDescriptor.GetAttributes(tool)[typeof(InheritanceAttribute)]; + if (ia != null && (ia.InheritanceLevel == InheritanceLevel.Inherited || ia.InheritanceLevel == InheritanceLevel.InheritedReadOnly)) + { + return; + } + } + + if (tool == null && !(primarySelection is ToolStripItem)) + { + RestoreCommands(); + // Reset the cached item... + SelectedDesignerControl = null; + } + } + + /// + /// Called when the current selection changes. Here we determine what commands can and can't be enabled. + /// + private void OnSelectionChanged(object sender, EventArgs e) + { + if (!(SelectionService.PrimarySelection is Component primarySelection)) + { + primarySelection = SelectedDesignerControl as ToolStripItem; + } + ToolStrip tool = primarySelection as ToolStrip; + if (tool != null) + { + InheritanceAttribute ia = (InheritanceAttribute)TypeDescriptor.GetAttributes(tool)[typeof(InheritanceAttribute)]; + if (ia != null && (ia.InheritanceLevel == InheritanceLevel.Inherited || ia.InheritanceLevel == InheritanceLevel.InheritedReadOnly)) + { + return; + } + } + + if (tool != null || primarySelection is ToolStripItem) + { + // Remove the Panel if any + BehaviorService behaviorService = (BehaviorService)_provider.GetService(typeof(BehaviorService)); + if (behaviorService != null) + { + DesignerActionUI designerUI = behaviorService.DesignerActionUI; + if (designerUI != null) + { + designerUI.HideDesignerActionPanel(); + } + } + AddCommands(); + } + } + + // helper function to select the next item. + public void ProcessKeySelect(bool reverse, MenuCommand cmd) + { + ISelectionService selSvc = SelectionService; + if (selSvc != null) + { + if (!(selSvc.PrimarySelection is ToolStripItem item)) + { + item = SelectedDesignerControl as ToolStripItem; + } + // Process Keys only if we are a ToolStripItem and the TemplateNode is not in Insitu Mode. + if (item != null) + { + if (!ProcessRightLeft(!reverse)) + { + RotateTab(reverse); + return; + } + return; + } + else if (item == null && selSvc.PrimarySelection is ToolStrip) + { + RotateTab(reverse); + } + } + } + + /// + /// This is the private helper function which is used to select the toolStripItem in the 'right' direction. + /// + private bool ProcessRightLeft(bool right) + { + Control ctl; + object targetSelection = null; + object currentSelection; + ISelectionService selSvc = SelectionService; + IDesignerHost host = Host; + if (selSvc == null || host == null || !(host.RootComponent is Control)) + { + return false; + } + + currentSelection = selSvc.PrimarySelection; + if (_shiftPressed && ShiftPrimaryItem != null) + { + currentSelection = ShiftPrimaryItem; + } + if (currentSelection == null) + { + currentSelection = SelectedDesignerControl; + } + + ctl = currentSelection as Control; + if (targetSelection == null && ctl == null) + { + ToolStripItem toolStripItem = selSvc.PrimarySelection as ToolStripItem; + if (_shiftPressed && ShiftPrimaryItem != null) + { + toolStripItem = ShiftPrimaryItem as ToolStripItem; + } + if (toolStripItem == null) + { + toolStripItem = SelectedDesignerControl as ToolStripItem; + } + if (toolStripItem is DesignerToolStripControlHost && toolStripItem.GetCurrentParent() is ToolStripDropDown parent) + { + if (parent != null) + { + if (right) + { + //no where to go .. since we are on DesignerToolStripControlHost for DropDown. + } + else + { + if (parent is ToolStripOverflow) + { + targetSelection = GetNextItem(parent, toolStripItem, ArrowDirection.Left); + } + else + { + targetSelection = parent.OwnerItem; + } + } + } + if (targetSelection != null) + { + SetSelection(targetSelection); + return true; + } + } + else + { + ToolStripItem item = selSvc.PrimarySelection as ToolStripItem; + if (_shiftPressed && ShiftPrimaryItem != null) + { + item = ShiftPrimaryItem as ToolStripDropDownItem; + } + if (item == null) + { + item = SelectedDesignerControl as ToolStripDropDownItem; + } + if (item != null && item.IsOnDropDown) + { + bool menusCascadeRight = SystemInformation.RightAlignedMenus; + if ((menusCascadeRight && right) || (!menusCascadeRight && right)) + { + if (item is ToolStripDropDownItem dropDownItem) + { + targetSelection = GetNextItem(dropDownItem.DropDown, null, ArrowDirection.Right); + if (targetSelection != null) + { + SetSelection(targetSelection); + //Open the DropDown after the Selection is Completed. + if (!(dropDownItem.DropDown.Visible)) + { + if (host.GetDesigner(dropDownItem) is ToolStripMenuItemDesigner designer) + { + designer.InitializeDropDown(); + + } + } + return true; + } + } + } + if (!right && !menusCascadeRight) + { + ToolStripItem owner = ((ToolStripDropDown)item.Owner).OwnerItem; + if (!owner.IsOnDropDown) + { + ToolStrip mainTool = owner.GetCurrentParent(); + targetSelection = GetNextItem(mainTool, owner, ArrowDirection.Left); + + } + else + { + targetSelection = owner; + } + if (targetSelection != null) + { + SetSelection(targetSelection); + return true; + } + } + } + } + } + return false; + } + + + /// + /// This is the private helper function which is used to select the toolStripItem in the 'down' direction. + /// + public void ProcessUpDown(bool down) + { + Control ctl; + object targetSelection = null; + object currentSelection; + ISelectionService selSvc = SelectionService; + IDesignerHost host = Host; + if (selSvc == null || host == null || !(host.RootComponent is Control)) + { + return; + } + + currentSelection = selSvc.PrimarySelection; + if (_shiftPressed && ShiftPrimaryItem != null) + { + currentSelection = ShiftPrimaryItem; + } + + //Check for ContextMenuStrip first... + if (currentSelection is ContextMenuStrip contextMenu) + { + if (down) + { + targetSelection = GetNextItem(contextMenu, null, ArrowDirection.Down); + SetSelection(targetSelection); + } + return; + } + + if (currentSelection == null) + { + currentSelection = SelectedDesignerControl; + } + ctl = currentSelection as Control; + + if (targetSelection == null && ctl == null) + { + ToolStripItem item = selSvc.PrimarySelection as ToolStripItem; + if (_shiftPressed && ShiftPrimaryItem != null) + { + item = ShiftPrimaryItem as ToolStripItem; + } + if (item == null) + { + item = SelectedDesignerControl as ToolStripItem; + } + ToolStripDropDown parentToMoveOn = null; + if (item != null) + { + if (item is DesignerToolStripControlHost) + { + // so now if Down Arrow is pressed .. open the dropDown.... + if (down) + { + if (SelectedDesignerControl is DesignerToolStripControlHost controlHost) + { + if (controlHost.Control is ToolStripTemplateNode.TransparentToolStrip tool) + { + ToolStripTemplateNode node = tool.TemplateNode; + if (node != null) + { + node.ShowDropDownMenu(); + return; + } + } + } + } + else + { + parentToMoveOn = item.GetCurrentParent() as ToolStripDropDown; + } + } + else + { + ToolStripDropDownItem dropDownItem = item as ToolStripDropDownItem; + if (dropDownItem != null && !dropDownItem.IsOnDropDown) + { + parentToMoveOn = dropDownItem.DropDown; + item = null; + } + else if (dropDownItem != null) + { + parentToMoveOn = ((dropDownItem.Placement == ToolStripItemPlacement.Overflow) ? dropDownItem.Owner.OverflowButton.DropDown : dropDownItem.Owner) as ToolStripDropDown; + item = dropDownItem; + } + if (dropDownItem == null) + { + parentToMoveOn = item.GetCurrentParent() as ToolStripDropDown; + } + } + + if (parentToMoveOn != null) //This will be null for NON dropDownItems... + { + if (down) + { + targetSelection = GetNextItem(parentToMoveOn, item, ArrowDirection.Down); + //lets check the index to know if we have wrapped around... only on NON ContextMenuStrip, ToolStripDropDown (added from toolbox) + if (parentToMoveOn.OwnerItem != null) // this can be null for overflow.... + { + if (!(parentToMoveOn.OwnerItem.IsOnDropDown) && (parentToMoveOn.OwnerItem.Owner != null && parentToMoveOn.OwnerItem.Owner.Site != null)) + { + if (targetSelection is ToolStripItem newSelection) + { + // We are wrapping around on the FirstDropDown select OwnerItem... + if (parentToMoveOn.Items.IndexOf(newSelection) != -1 && parentToMoveOn.Items.IndexOf(newSelection) <= parentToMoveOn.Items.IndexOf(item)) + { + targetSelection = parentToMoveOn.OwnerItem; + } + } + } + } + + if (_shiftPressed && SelectionService.GetComponentSelected(targetSelection)) + { + SelectionService.SetSelectedComponents(new object[] { ShiftPrimaryItem, targetSelection }, SelectionTypes.Remove); + } + } + else + { + // We dont want to WRAP around for items on toolStrip Overflow, if the currentSelection is the topMost item on the Overflow, but select the one on the PARENT toolStrip. + if (parentToMoveOn is ToolStripOverflow) + { + ToolStripItem firstItem = GetNextItem(parentToMoveOn, null, ArrowDirection.Down); + if (item == firstItem) + { + if (item.Owner is ToolStrip owner) + { + targetSelection = GetNextItem(owner, ((ToolStripDropDown)parentToMoveOn).OwnerItem, ArrowDirection.Left); + } + } + else + { + targetSelection = GetNextItem(parentToMoveOn, item, ArrowDirection.Up); + } + } + else + { + targetSelection = GetNextItem(parentToMoveOn, item, ArrowDirection.Up); + } + + //lets check the index to know if we have wrapped around... + if (parentToMoveOn.OwnerItem != null) // this can be null for overflow.... + { + if (!(parentToMoveOn.OwnerItem.IsOnDropDown) && (parentToMoveOn.OwnerItem.Owner != null && parentToMoveOn.OwnerItem.Owner.Site != null)) + { + if (targetSelection is ToolStripItem newSelection && item != null) + { + // We are wrapping around on the FirstDropDown select OwnerItem... + if (parentToMoveOn.Items.IndexOf(newSelection) != -1 && parentToMoveOn.Items.IndexOf(newSelection) >= parentToMoveOn.Items.IndexOf(item)) + { + targetSelection = parentToMoveOn.OwnerItem; + } + } + } + } + + if (_shiftPressed && SelectionService.GetComponentSelected(targetSelection)) + { + SelectionService.SetSelectedComponents(new object[] { ShiftPrimaryItem, targetSelection }, SelectionTypes.Remove); + } + } + if (targetSelection != null && targetSelection != item) + { + SetSelection(targetSelection); + } + } + } + } + } + + // caches the old commands from the menuCommand service. + private void PopulateOldCommands() + { + if (_oldCommands == null) + { + _oldCommands = new ArrayList(); + } + IMenuCommandService mcs = MenuService; + if (mcs != null) + { + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeySelectNext)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeySelectPrevious)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeyDefaultAction)); + + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeyMoveUp)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeyMoveDown)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeyMoveLeft)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeyMoveRight)); + + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeyNudgeUp)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeyNudgeDown)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeyNudgeLeft)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeyNudgeRight)); + + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeySizeWidthIncrease)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeySizeHeightIncrease)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeySizeWidthDecrease)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeySizeHeightDecrease)); + + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeyCancel)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeyReverseCancel)); + _oldCommands.Add(mcs.FindCommand(StandardCommands.Copy)); + _oldCommands.Add(mcs.FindCommand(StandardCommands.SelectAll)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.KeyInvokeSmartTag)); + + _oldCommands.Add(mcs.FindCommand(StandardCommands.Cut)); + _oldCommands.Add(mcs.FindCommand(MenuCommands.Delete)); + } + } + + // pupulates a list of our custom commands to be added to menu command service. + private void PopulateNewCommands() + { + if (_newCommands == null) + { + _newCommands = new ArrayList(); + } + + _newCommands.Add(new MenuCommand(new EventHandler(OnKeySelect), MenuCommands.KeySelectNext)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeySelect), MenuCommands.KeySelectPrevious)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyDefault), MenuCommands.KeyDefaultAction)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyEdit), MenuCommands.EditLabel)); + + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyMove), MenuCommands.KeyMoveUp)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyMove), MenuCommands.KeyMoveDown)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyMove), MenuCommands.KeyMoveLeft)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyMove), MenuCommands.KeyMoveRight)); + + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyMove), MenuCommands.KeyNudgeUp)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyMove), MenuCommands.KeyNudgeDown)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyMove), MenuCommands.KeyNudgeLeft)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyMove), MenuCommands.KeyNudgeRight)); + + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyMove), MenuCommands.KeySizeWidthIncrease)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyMove), MenuCommands.KeySizeHeightIncrease)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyMove), MenuCommands.KeySizeWidthDecrease)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyMove), MenuCommands.KeySizeHeightDecrease)); + + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyCancel), MenuCommands.KeyCancel)); + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyCancel), MenuCommands.KeyReverseCancel)); + _newCommands.Add(new MenuCommand(new EventHandler(OnCommandCopy), StandardCommands.Copy)); + _newCommands.Add(new MenuCommand(new EventHandler(OnCommandSelectAll), StandardCommands.SelectAll)); + + _newCommands.Add(new MenuCommand(new EventHandler(OnCommandHome), MenuCommands.KeyHome)); + _newCommands.Add(new MenuCommand(new EventHandler(OnCommandEnd), MenuCommands.KeyEnd)); + _newCommands.Add(new MenuCommand(new EventHandler(OnCommandHome), MenuCommands.KeyShiftHome)); + _newCommands.Add(new MenuCommand(new EventHandler(OnCommandEnd), MenuCommands.KeyShiftEnd)); + + //Command for opening the DropDown for templatenode. + _newCommands.Add(new MenuCommand(new EventHandler(OnKeyShowDesignerActions), MenuCommands.KeyInvokeSmartTag)); + + _newCommands.Add(new MenuCommand(new EventHandler(OnCommandCopy), StandardCommands.Cut)); + _newCommands.Add(new MenuCommand(new EventHandler(OnCommandDelete), MenuCommands.Delete)); + } + + // restores the old commands back into the menu command service. + public void RestoreCommands() + { + IMenuCommandService mcs = MenuService; + if (mcs != null & _commandsAdded) + { + //Remove the new Commands + if (_newCommands != null) + { + foreach (MenuCommand newCommand in _newCommands) + { + mcs.RemoveCommand(newCommand); + } + } + // Add old Commands + if (_oldCommands != null) + { + foreach (MenuCommand oldCommand in _oldCommands) + { + if (oldCommand != null && mcs.FindCommand(oldCommand.CommandID) == null) + { + mcs.AddCommand(oldCommand); + } + } + } + + if (_newCommandPaste != null) + { + mcs.RemoveCommand(_newCommandPaste); + _newCommandPaste = null; + } + + if (_oldCommandPaste != null && mcs.FindCommand(_oldCommandPaste.CommandID) == null) + { + mcs.AddCommand(_oldCommandPaste); + _oldCommandPaste = null; + } + _commandsAdded = false; + } + } + + internal void ResetActiveTemplateNodeSelectionState() + { + if (SelectedDesignerControl != null) + { + if (SelectedDesignerControl is DesignerToolStripControlHost curDesignerNode) + { + curDesignerNode.RefreshSelectionGlyph(); + } + } + } + + /// + /// Disposes of this object, removing all commands from the menu service. + /// + public void RemoveCommands() + { + IMenuCommandService mcs = MenuService; + if (mcs != null && _commandsAdded) + { + //Remove our Commands... + if (_newCommands != null) + { + foreach (MenuCommand newCommand in _newCommands) + { + mcs.RemoveCommand(newCommand); + } + } + } + if (_newCommandPaste != null) + { + mcs.RemoveCommand(_newCommandPaste); + _newCommandPaste = null; + } + if (_oldCommandPaste != null) + { + _oldCommandPaste = null; + } + + if (_newCommands != null) + { + _newCommands.Clear(); + _newCommands = null; + } + if (_oldCommands != null) + { + _oldCommands.Clear(); + _oldCommands = null; + } + + if (_selectionService != null) + { + _selectionService.SelectionChanging -= new EventHandler(OnSelectionChanging); + _selectionService.SelectionChanged -= new EventHandler(OnSelectionChanged); + _selectionService = null; + } + + if (_componentChangeSvc != null) + { + _componentChangeSvc.ComponentRemoved -= new ComponentEventHandler(OnComponentRemoved); + _componentChangeSvc = null; + } + + _currentSelection = null; + _shiftPrimary = null; + _provider = null; + _menuCommandService = null; + _activeTemplateNode = null; + } + + /// + /// This function allows the service to select the parent for the selected Item. + /// + private void RotateParent(bool backwards) + { + Control current = null; + object next = null; + ToolStripItem toolStripItem = null; + ISelectionService selSvc = SelectionService; + IDesignerHost host = Host; + if (selSvc == null || host == null || !(host.RootComponent is Control)) + { + return; + } + + IContainer container = host.Container; + if (!(selSvc.PrimarySelection is Control component)) + { + component = SelectedDesignerControl as Control; + } + if (component != null) + { + current = component; + } + else + { + toolStripItem = selSvc.PrimarySelection as ToolStripItem; + if (toolStripItem == null) + { + toolStripItem = SelectedDesignerControl as ToolStripItem; + } + if (toolStripItem == null) + { + current = (Control)host.RootComponent; + } + } + + if (backwards) + { + if (current != null) + { + if (current.Controls.Count > 0) + { + next = current.Controls[0]; + } + else + { + next = current; + } + } + else if (toolStripItem != null) + { + next = toolStripItem.Owner.Controls[0]; + } + } + else + { + if (current != null) + { + next = current.Parent; + if (!(next is Control nextControl) || nextControl.Site == null || nextControl.Site.Container != container) + { + next = current; + } + } + else if (toolStripItem != null) + { + if (toolStripItem.IsOnDropDown && toolStripItem.Placement != ToolStripItemPlacement.Overflow) + { + next = ((ToolStripDropDown)toolStripItem.Owner).OwnerItem; + } + else if (toolStripItem.IsOnDropDown && toolStripItem.Placement == ToolStripItemPlacement.Overflow) + { + ToolStrip owner = toolStripItem.Owner; + if (owner != null) + { + owner.OverflowButton.HideDropDown(); + } + next = toolStripItem.Owner; + + } + else + { + next = toolStripItem.Owner; + } + } + } + + if (next is DesignerToolStripControlHost) + { + SelectedDesignerControl = next; + selSvc.SetSelectedComponents(null, SelectionTypes.Replace); + } + else + { + SelectedDesignerControl = null; + selSvc.SetSelectedComponents(new object[] { next }, SelectionTypes.Replace); + } + } + + /// + /// This function allows the service to rotate the TabSelection when TAB key is pressed. + /// + // Okay to suppress because of complex code path + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + public void RotateTab(bool backwards) + { + Control ctl; + Control baseCtl; + object targetSelection = null; + object currentSelection; + ISelectionService selSvc = SelectionService; + IDesignerHost host = Host; + if (selSvc == null || host == null || !(host.RootComponent is Control)) + { + return; + } + + IContainer container = host.Container; + baseCtl = (Control)host.RootComponent; + // We must handle two cases of logic here. We are responsible for handling selection within ourself, and also for components on the tray. For our own tabbing around, we want to go by tab-order. When we get to the end of the form, however, we go by selection order into the tray. And, when we're at the end of the tray we start back at the form. We must reverse this logic to go backwards. + currentSelection = selSvc.PrimarySelection; + if (_shiftPressed && ShiftPrimaryItem != null) + { + currentSelection = ShiftPrimaryItem; + } + if (currentSelection == null) + { + currentSelection = SelectedDesignerControl; + // If we are on templateNode and tabbing ahead ... the select the next Control on the parent ... + if (currentSelection != null) + { + if (currentSelection is DesignerToolStripControlHost templateNodeItem && (!templateNodeItem.IsOnDropDown || (templateNodeItem.IsOnDropDown && templateNodeItem.IsOnOverflow))) + { + ctl = templateNodeItem.Owner; + if ((ctl.RightToLeft != RightToLeft.Yes && !backwards) || (ctl.RightToLeft == RightToLeft.Yes && backwards)) + { + targetSelection = GetNextControlInTab(baseCtl, ctl, !backwards); + if (targetSelection == null) + { + ComponentTray tray = (ComponentTray)_provider.GetService(typeof(ComponentTray)); + if (tray != null) + { + targetSelection = tray.GetNextComponent((IComponent)currentSelection, !backwards); + if (targetSelection != null) + { + ControlDesigner controlDesigner = host.GetDesigner((IComponent)targetSelection) as ControlDesigner; + // In Whidbey controls like ToolStrips have componentTray presence, So dont select them again through compoenent tray since here we select only Components. Hence only components that have ComponentDesigners should be selected via the ComponentTray. + while (controlDesigner != null) + { + // if the targetSelection from the Tray is a control .. try the next one. + targetSelection = tray.GetNextComponent((IComponent)targetSelection, !backwards); + if (targetSelection != null) + { + controlDesigner = host.GetDesigner((IComponent)targetSelection) as ControlDesigner; + } + else + { + controlDesigner = null; + } + } + } + } + if (targetSelection == null) + { + targetSelection = baseCtl; + } + } + } + } + } + } + ctl = currentSelection as Control; + //Added New Code for WinBar Tabbing.. + if (targetSelection == null && ctl is ToolStrip wb) + { + ToolStripItemCollection collection = wb.Items; + if (collection != null) + { + if (!backwards) + { + targetSelection = collection[0]; + } + else + { + targetSelection = collection[wb.Items.Count - 1]; + } + } + } + // ctl is NOT A CONTROL ... so its Component. Try this for WinBarItem. + if (targetSelection == null && ctl == null) + { + ToolStripItem item = selSvc.PrimarySelection as ToolStripItem; + if (_shiftPressed && ShiftPrimaryItem != null) + { + item = ShiftPrimaryItem as ToolStripItem; + } + if (item == null) + { + item = SelectedDesignerControl as ToolStripItem; + } + if (item != null && item.IsOnDropDown && item.Placement != ToolStripItemPlacement.Overflow) + { + // You come here only for DesignerToolStripControlHost on the DropDown ... + Debug.WriteLineIf(item is DesignerToolStripControlHost, " Why are we here for non DesignerMenuItem??"); + if (item is DesignerToolStripControlHost designerItem) + { + ToolStripItem parentItem = ((ToolStripDropDown)designerItem.Owner).OwnerItem; + ToolStripMenuItemDesigner designer = host.GetDesigner(parentItem) as ToolStripMenuItemDesigner; + ToolStripDropDown dropDown = designer.GetFirstDropDown((ToolStripDropDownItem)parentItem); + if (dropDown != null) //the DesignerItem is on DropDown.... + { + item = dropDown.OwnerItem; + } + else //The DesignerItem is on FirstDropDown... + { + item = parentItem; + } + } + } + if (item != null && !(item is DesignerToolStripControlHost)) + { + ToolStrip parent = item.GetCurrentParent(); + if (parent != null) + { + if (backwards) + { + // We are item on ToolStripOverflow... + if (parent is ToolStripOverflow) + { + ToolStripItem firstItem = GetNextItem(parent, null, ArrowDirection.Down); + if (item == firstItem) + { + if (item.Owner is ToolStrip owner) + { + targetSelection = GetNextItem(owner, ((ToolStripDropDown)parent).OwnerItem, ArrowDirection.Left); + } + } + else + { + targetSelection = GetNextItem(parent, item, ArrowDirection.Left); + } + } + // check if this is the first item .. if so move out of ToolStrip... + else if (item == parent.Items[0] && parent.RightToLeft != RightToLeft.Yes) + { + // If Shift Pressed ... stop at 1st Item.. + if (_shiftPressed) + { + return; + } + targetSelection = GetNextControlInTab(baseCtl, parent, !backwards); + if (targetSelection == null) + { + ComponentTray tray = (ComponentTray)_provider.GetService(typeof(ComponentTray)); + if (tray != null) + { + targetSelection = tray.GetNextComponent((IComponent)currentSelection, !backwards); + if (targetSelection != null) + { + ControlDesigner controlDesigner = host.GetDesigner((IComponent)targetSelection) as ControlDesigner; + // In Whidbey controls like ToolStrips have componentTray presence, So dont select them again through compoenent tray since here we select only Components. Hence only components that have ComponentDesigners should be selected via the ComponentTray. + while (controlDesigner != null) + { + // if the targetSelection from the Tray is a control .. try the next one. + targetSelection = tray.GetNextComponent((IComponent)targetSelection, !backwards); + if (targetSelection != null) + { + controlDesigner = host.GetDesigner((IComponent)targetSelection) as ControlDesigner; + } + else + { + controlDesigner = null; + } + } + } + } + if (targetSelection == null) + { + targetSelection = baseCtl; + } + } + } + else + { + targetSelection = GetNextItem(parent, item, ArrowDirection.Left); + if (_shiftPressed && SelectionService.GetComponentSelected(targetSelection)) + { + SelectionService.SetSelectedComponents(new object[] { ShiftPrimaryItem, targetSelection }, SelectionTypes.Remove); + } + } + } + else + { + // We are item on ToolStripOverflow... + if (parent is ToolStripOverflow) + { + targetSelection = GetNextItem(parent, item, ArrowDirection.Down); + } + else if (item == parent.Items[0] && parent.RightToLeft == RightToLeft.Yes) + { + // If Shift Pressed ... stop at 1st Item.. + if (_shiftPressed) + { + return; + } + targetSelection = GetNextControlInTab(baseCtl, parent, !backwards); + // this is the First control in TabOrder... Select the Form.. + if (targetSelection == null) + { + targetSelection = baseCtl; + } + } + else + { + targetSelection = GetNextItem(parent, item, ArrowDirection.Right); + if (_shiftPressed && SelectionService.GetComponentSelected(targetSelection)) + { + SelectionService.SetSelectedComponents(new object[] { ShiftPrimaryItem, targetSelection }, SelectionTypes.Remove); + } + } + } + } + } + // This is a DesignerToolStripControlHost on the Main ToolStrip. + else if (item != null) + { + ToolStrip parent = item.GetCurrentParent(); + if (parent != null) + { + // flip the semantics of bakcwards... + if (parent.RightToLeft == RightToLeft.Yes) + { + backwards = !backwards; + } + if (backwards) + { + ToolStripItemCollection collection = parent.Items; + if (collection.Count >= 2) + { + targetSelection = collection[collection.Count - 2]; + } + else + { + targetSelection = GetNextControlInTab(baseCtl, parent, !backwards); + } + } + else + { + ToolStripItemCollection collection = parent.Items; + targetSelection = collection[0]; + } + } + } + } + + if (targetSelection == null && ctl != null && (baseCtl.Contains(ctl) || baseCtl == currentSelection)) + { + // Our current selection is a control. Select the next control in the z-order. + while (null != (ctl = GetNextControlInTab(baseCtl, ctl, !backwards))) + { + if (ctl.Site != null && ctl.Site.Container == container && !(ctl is ToolStripPanel)) + { + break; + } + } + targetSelection = ctl; + } + + if (targetSelection == null) + { + ComponentTray tray = (ComponentTray)_provider.GetService(typeof(ComponentTray)); + if (tray != null) + { + targetSelection = tray.GetNextComponent((IComponent)currentSelection, !backwards); + } + + if (targetSelection == null || targetSelection == currentSelection) + { + targetSelection = baseCtl; + } + } + + //Special Casing since moving to TemplateNode to TemplateNode is moving from null selection to null selection. + if (targetSelection is DesignerToolStripControlHost && currentSelection is DesignerToolStripControlHost) + { + SelectedDesignerControl = targetSelection; + selSvc.SetSelectedComponents(new object[] { targetSelection }, SelectionTypes.Replace); + selSvc.SetSelectedComponents(null, SelectionTypes.Replace); + } + else + { + SetSelection(targetSelection); + } + } + + // Select components. + private void SelectItems(ToolStrip parent) + { + object[] totalObjects = new object[parent.Items.Count - 1]; + for (int i = 0; i < parent.Items.Count - 1; i++) + { + if (parent.Items[i] is DesignerToolStripControlHost) + { + continue; + } + totalObjects[i] = parent.Items[i]; + } + SelectionService.SetSelectedComponents(totalObjects, SelectionTypes.Replace); + } + + // Helper function called by all handlers to select the target Selection. + private void SetSelection(object targetSelection) + { + ISelectionService selSvc = SelectionService; + if (selSvc != null) + { + //Cache original selection + ICollection originalSelComps = selSvc.GetSelectedComponents(); + // Add the TemplateNode to the Selection if it is currently Selected as the GetSelectedComponents wont do it for us. + ArrayList origSel = new ArrayList(originalSelComps); + if (origSel.Count == 0) + { + if (SelectedDesignerControl != null) + { + origSel.Add(SelectedDesignerControl); + } + } + + if (targetSelection is DesignerToolStripControlHost) + { + if (!_shiftPressed) + { + SelectedDesignerControl = targetSelection; + selSvc.SetSelectedComponents(null, SelectionTypes.Replace); + } + } + else + { + if (targetSelection is ToolStripOverflowButton overFlowButton) + { + SelectedDesignerControl = null; + + if (overFlowButton != null) + { + overFlowButton.ShowDropDown(); + } + object newSelection = GetNextItem(overFlowButton.DropDown, null, ArrowDirection.Down); + + if (!_shiftPressed) + { + ShiftPrimaryItem = null; + selSvc.SetSelectedComponents(new object[] { newSelection }, SelectionTypes.Replace); + } + else + { + selSvc.SetSelectedComponents(new object[] { newSelection }); + ShiftPrimaryItem = targetSelection; + } + } + else + { + SelectedDesignerControl = null; + if (!_shiftPressed) + { + ShiftPrimaryItem = null; + selSvc.SetSelectedComponents(new object[] { targetSelection }, SelectionTypes.Replace); + } + else + { + selSvc.SetSelectedComponents(new object[] { targetSelection }); + ShiftPrimaryItem = targetSelection; + } + } + } + + // Invalidate old & new selection. + ToolStripDesignerUtils.InvalidateSelection(origSel, targetSelection as ToolStripItem, _provider, _shiftPressed); + } + //reset the shiftPressed since we end selection + _shiftPressed = false; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs new file mode 100644 index 00000000000..90e6275bcd8 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs @@ -0,0 +1,2793 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.ComponentModel.Design.Serialization; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Windows.Forms.Design.Behavior; + +namespace System.Windows.Forms.Design +{ + /// + /// Designer for ToolStripMenuItems. + /// + internal class ToolStripMenuItemDesigner : ToolStripDropDownItemDesigner + { + private const int GLYPHINSET = 2; + // This is the typeHereNode that appears to the bottom and right of each commited ToolStripDropDownItem + private DesignerToolStripControlHost typeHereNode; + private ToolStripTemplateNode typeHereTemplateNode = null; + // This is the TemplateNode.EditorToolStrip. The editor ToolStrip is encapsulated in a ToolStripControlHost and then added as a ToolStripDropDownItem on the current ToolStripDropDownItems DropDown. + private DesignerToolStripControlHost commitedEditorNode; + // Actual InSitu Editor. This is created for every selected ToolStripDropDownItem and is DISPOSED immediately after the item comes out of InSitu edit Mode. + private ToolStripTemplateNode commitedTemplateNode; + + // DesignerHost for current Component + private IDesignerHost designerHost; + private ToolStripItem parentItem = null; + private ToolStripAdornerWindowService toolStripAdornerWindowService = null; + private ToolStripKeyboardHandlingService keyboardHandlingService = null; + //Make Selection service a class level member.. used a lot. + private ISelectionService selSvc = null; + //DesignerTrnasaction used for removing Items + private DesignerTransaction _pendingTransaction = null; + private bool fireComponentChanged = false; + //indicates that we are adding new MenuItem .. + private bool componentAddingFired = false; + //new item index + private int indexToInsertNewItem = -1; + //only used by DropDownMenu, DropDown or ContextMenuStrip. + private DesignerTransaction insertMenuItemTransaction; + + // We want one parent transaction when the item is added by InSitu editing which is now supported only for MenuItems. This parent transaction would include + // - Adding the DummyItem + // - Swapping the DummyItem with the InsItu + // - Committing the new Item with the text entered in the Insitu. + private DesignerTransaction newMenuItemTransaction; + //DropDownToInvalidate + private Rectangle dropDownSizeToInvalidate = Rectangle.Empty; + //DropDownToInvalidate while ComponentRemove + private Rectangle boundsToInvalidateOnRemove = Rectangle.Empty; + private ToolStripDropDownGlyph rootControlGlyph; + private bool initialized = false; + + // Hook on the Undoing and Undone Events... + private UndoEngine undoEngine = null; + private bool undoingCalled = false; + private bool addingDummyItem = false; + + //custom DropDown + private ToolStripDropDown customDropDown = null; + private bool dropDownSet = false; + private SerializationStore serializedDataForDropDownItems = null; + private bool dropDownSetFailed = false; + + /// + /// The ToolStripDropDownItems are the associated components. + /// We want those to come with in any cut, copy opreations. + /// + public override ICollection AssociatedComponents + { + get + { + ArrayList items = new ArrayList(); + // Return the list only if the DropDown is AutoGenerated. else the DropDown property set/get will handle it. + if (MenuItem.DropDown.IsAutoGenerated) + { + foreach (ToolStripItem item in MenuItem.DropDownItems) + { + if (!(item is DesignerToolStripControlHost addNewItem)) + { + items.Add(item); + } + } + } + return (ICollection)items; + } + } + + /// + /// ShadowProperty. + /// + private bool CheckOnClick + { + get => (bool)ShadowProperties["CheckOnClick"]; + set => ShadowProperties["CheckOnClick"] = value; + } + + private bool DoubleClickEnabled + { + get => (bool)ShadowProperties["DoubleClickEnabled"]; + set => ShadowProperties["DoubleClickEnabled"] = value; + } + + private ToolStripDropDown DropDown + { + get => customDropDown; + set + { + dropDownSetFailed = false; + if (IsParentDropDown(value)) + { + dropDownSetFailed = true; + throw new ArgumentException(string.Format(SR.InvalidArgument, "DropDown", value.ToString())); + } + //Check if DropDown Exists .. and if yes then Close the DropDown + if (MenuItem.DropDown != null) + { + RemoveTypeHereNode(MenuItem); + // remove and destroy all the items... + if (MenuItem.DropDown.IsAutoGenerated && MenuItem.DropDownItems.Count > 0) + { + //Serialize all the DropDownItems for this Item.... + if (GetService(typeof(ComponentSerializationService)) is ComponentSerializationService serializationService) + { + serializedDataForDropDownItems = serializationService.CreateStore(); + foreach (ToolStripItem item in MenuItem.DropDownItems) + { + //Dont Serialize the DesignerToolStripControlHost... + if (!(item is DesignerToolStripControlHost)) + { + serializationService.Serialize(serializedDataForDropDownItems, item); + } + } + //close the SerializationStore to Serialize Items.. + serializedDataForDropDownItems.Close(); + } + + ToolStripItem[] items = new ToolStripItem[MenuItem.DropDownItems.Count]; + MenuItem.DropDownItems.CopyTo(items, 0); + foreach (ToolStripItem item in items) + { + MenuItem.DropDownItems.Remove(item); + designerHost.DestroyComponent(item); + } + } + MenuItem.HideDropDown(); + } + + // Set The DropDown + MenuItem.DropDown = value; + //Set the Shadow Property + customDropDown = value; + // If DropDown is Set to null and we have valid serializedData, then use it to recreate Items. + if (value == null && !dropDownSet && serializedDataForDropDownItems != null) + { + try + { + // turn off Adding/Added events listened to by the ToolStripDesigner... + ToolStripDesigner.s_autoAddNewItems = false; + CreatetypeHereNode(); + if (GetService(typeof(ComponentSerializationService)) is ComponentSerializationService serializationService) + { + serializationService.Deserialize(serializedDataForDropDownItems); + serializedDataForDropDownItems = null; + } + } + finally + { + // turn on Adding/Added events listened to by the ToolStripDesigner... + ToolStripDesigner.s_autoAddNewItems = true; + } + } + MenuItem.DropDown.OwnerItem = MenuItem; + //hook up to the Disposed event ... + MenuItem.DropDown.Disposed += new EventHandler(OnDropDownDisposed); + // show the dropDown. + if (MenuItem.Equals(selSvc.PrimarySelection)) + { + //Add TypeHereNode & show the Dropdown + InitializeDropDown(); + } + } + } + + /// + /// ToolStripEditorManager used this internal property to + /// Activate the editor. + /// + internal override ToolStripTemplateNode Editor + { + get + { + if (base.Editor != null) + { + return base.Editor; + } + if (commitedTemplateNode != null) + { + return commitedTemplateNode; + } + return typeHereTemplateNode; + } + set => commitedTemplateNode = value; + } + + /// + /// True if the MenuItem is on ContextMenu. + /// + private bool IsOnContextMenu + { + get + { + ToolStrip mainStrip = GetMainToolStrip(); + if (mainStrip != null && mainStrip.Site != null && !(mainStrip is ContextMenuStrip)) + { + return false; + } + return true; + } + } + + /// + /// Easy method for getting to the ToolStripDropDownItem + /// + private ToolStripDropDownItem MenuItem + { + get => ToolStripItem as ToolStripDropDownItem; + } + + /// + /// This property is true when the OwnerItem is selected during COPY & PASTE operations. + /// + private bool MenuItemSelected + { + get + { + // check to see if the primary selection is the ToolStrip or one of it's children. + if (selSvc != null) + { + object selectedItem = selSvc.PrimarySelection; + ToolStripItem toolItem; + if (selectedItem == null) + { + if (KeyboardHandlingService != null) + { + selectedItem = KeyboardHandlingService.SelectedDesignerControl; + } + toolItem = selectedItem as ToolStripItem; + } + else + { + toolItem = selectedItem as ToolStripItem; + } + if (toolItem != null) + { + // We always need to return the current selection if we are adding a DummyNode... + if (designerHost != null) + { + if (designerHost.GetDesigner(toolItem) is ToolStripItemDesigner designer) + { + if (designer.dummyItemAdded) + { + return (toolItem == MenuItem); + } + } + } + // We need to return parent if we are on DropDown... + if (toolItem.IsOnDropDown && toolItem.Owner is ToolStripDropDown) + { + ToolStripDropDownItem parentItem = ((ToolStripDropDown)toolItem.Owner).OwnerItem as ToolStripDropDownItem; + return (parentItem == MenuItem); + } + else + { + return (toolItem == MenuItem); + } + } + else if (selectedItem is ContextMenuStrip && + ((ContextMenuStrip)selectedItem).OwnerItem == this.Component && // VSO 214130--SelectedItem must belong to this designer + MenuItem.DropDown == selectedItem) + { + return true; + } + + } + return false; + } + } + + private ToolStripKeyboardHandlingService KeyboardHandlingService + { + get + { + if (keyboardHandlingService == null) + { + keyboardHandlingService = GetService(typeof(ToolStripKeyboardHandlingService)) as ToolStripKeyboardHandlingService; + } + return keyboardHandlingService; + } + } + + + /// + /// ParentComponent in case of MenuItems on the DropDown is the OwnerItem of the DropDown and not the ToolStripDropDown (since the DropDowns are not sited) + /// + protected override IComponent ParentComponent + { + get + { + if (ToolStripItem != null) + { + if (!ToolStripItem.IsOnOverflow && ToolStripItem.IsOnDropDown) + { + // If the dropDown is NOT AutoGenerated then its a dropdown the user has set or its a contextMenuStrip/ToolStripDropDownMenu/ToolStripDropDown that has been added from the ToolBox. In such a case dont return the ownerItem but the DropDown itself as the ParentComponent. + if (MenuItem.Owner is ToolStripDropDown dropDown) + { + if (dropDown.IsAutoGenerated) + { + return dropDown.OwnerItem; + } + else + { + return dropDown; + } + } + } + return base.ParentComponent; + } + return null; + } + } + + /// + /// Adds the dummy node for InSitu Edit. + /// + internal void AddNewTemplateNode(ToolStripDropDown dropDown) + { + + //Check if the DropDown contains a typehereNode.... + foreach (ToolStripItem currentItem in dropDown.Items) + { + if (currentItem is DesignerToolStripControlHost) + { + typeHereNode = (DesignerToolStripControlHost)currentItem; + } + } + if (typeHereNode != null) + { + dropDown.Items.Remove(typeHereNode); + } + // setup the MINIToolStrip host... + typeHereTemplateNode = new ToolStripTemplateNode(Component, SR.ToolStripDesignerTemplateNodeEnterText, null); + int width = typeHereTemplateNode.EditorToolStrip.Width; + typeHereNode = new DesignerToolStripControlHost(typeHereTemplateNode.EditorToolStrip); + typeHereTemplateNode.ControlHost = typeHereNode; + typeHereNode.AutoSize = false; + typeHereNode.Width = width; + dropDown.Items.Add(typeHereNode); + } + + // used by morphing in ToolStripItemDesigner ... hence internal method. + internal void AddItemBodyGlyph(ToolStripItem item) + { + if (item != null) + { + ToolStripItemDesigner dropDownItemDesigner = (ToolStripItemDesigner)designerHost.GetDesigner(item); + if (dropDownItemDesigner != null) + { + Rectangle bounds = dropDownItemDesigner.GetGlyphBounds(); + Behavior.Behavior toolStripBehavior = new ToolStripItemBehavior(); + // Initialize Glyph + ToolStripItemGlyph bodyGlyphForddItem = new ToolStripItemGlyph(item, dropDownItemDesigner, bounds, toolStripBehavior); + //Set the glyph for the item .. so that we can remove it later.... + dropDownItemDesigner.bodyGlyph = bodyGlyphForddItem; + //Add ItemGlyph to the Collection + if (toolStripAdornerWindowService != null) + { + toolStripAdornerWindowService.DropDownAdorner.Glyphs.Insert(0, bodyGlyphForddItem); + } + } + } + } + + // Add individual item Body Glyphs + private void AddBodyGlyphs(ToolStripDropDownItem item) + { + if (item != null) + { + ToolStripMenuItemDesigner itemDesigner = (ToolStripMenuItemDesigner)designerHost.GetDesigner(item); + if (itemDesigner != null) + { + foreach (ToolStripItem ddItem in item.DropDownItems) + { + AddItemBodyGlyph(ddItem); + } + } + } + } + + /// + /// This is called by the TemplateNode to Commit the Edit. This function creates a NEW ToolStripDropDownItem if we are committing a dummy node or else just replaces the TEXT and IMAGE if we are changing an existing MenuItem. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + internal override void CommitEdit(Type type, string text, bool commit, bool enterKeyPressed, bool tabKeyPressed) + { + IsEditorActive = false; + if (!(MenuItem.Owner is ToolStripDropDown) && base.Editor != null) //we are on Base Menustrip !! + { + base.CommitEdit(type, text, commit, enterKeyPressed, tabKeyPressed); + } + else if (commit) + { + int index = -1; + bool dummyItem = dummyItemAdded; + dummyItemAdded = false; + MenuItem.DropDown.SuspendLayout(); + if (commitedEditorNode != null) /// this means we have a valid node and we just changed some properties.. + { + index = MenuItem.DropDownItems.IndexOf(commitedEditorNode); + ToolStripItem editedItem = (ToolStripItem)MenuItem.DropDownItems[index + 1]; + // Remove TemplatENode + MenuItem.DropDown.Items.Remove(commitedEditorNode); + // Get rid of the templateNode... + if (commitedTemplateNode != null) + { + commitedTemplateNode.CloseEditor(); + commitedTemplateNode = null; + } + if (commitedEditorNode != null) + { + commitedEditorNode.Dispose(); + commitedEditorNode = null; + } + // If we have type "-" this means the user wants to add a Separator. + if (text == "-") + { + if (designerHost.GetDesigner(editedItem) is ToolStripItemDesigner itemDesigner) + { + try + { + editedItem = itemDesigner.MorphCurrentItem(typeof(ToolStripSeparator)); + //Remove the ActionGlyph Added by morphing + RemoveItemBodyGlyph(editedItem); + } + catch + { + if (newMenuItemTransaction != null) + { + try + { + newMenuItemTransaction.Cancel(); + } + catch //This will cause ROLLBACK and hence throw if the project is already in DEBUG and instuEdit was Active. + { + } + newMenuItemTransaction = null; + } + } + finally + { + if (newMenuItemTransaction != null) + { + newMenuItemTransaction.Commit(); + newMenuItemTransaction = null; + } + } + } + } + else + { + // We are adding the item through INSITU for the first time + if (dummyItem) + { + try + { + dummyItemAdded = true; + CreateNewItem(type, index, text); + designerHost.DestroyComponent(editedItem); + // One place where we need to call this explicitly since the selection doesnt change. + if (enterKeyPressed) + { + typeHereNode.SelectControl(); + } + } + catch + { + if (newMenuItemTransaction != null) + { + try + { + newMenuItemTransaction.Cancel(); + } + catch //This will cause ROLLBACK and hence throw if the project is already in DEBUG and instuEdit was Active. + { + } + newMenuItemTransaction = null; + } + } + finally + { + if (newMenuItemTransaction != null) + { + newMenuItemTransaction.Commit(); + newMenuItemTransaction = null; + } + dummyItemAdded = false; + } + } + else //We are editing item that was already present + { + // put the item back... + editedItem.Visible = true; + + //create our transaction + DesignerTransaction designerTransaction = designerHost.CreateTransaction(SR.ToolStripItemPropertyChangeTransaction); + try + { + //Change the properties + PropertyDescriptor textProp = TypeDescriptor.GetProperties(editedItem)["Text"]; + string oldValue = (string)textProp.GetValue(editedItem); + if (textProp != null && text != oldValue) + { + textProp.SetValue(editedItem, text); + } + } + catch + { + if (designerTransaction != null) + { + designerTransaction.Cancel(); + designerTransaction = null; + } + } + finally + { + if (designerTransaction != null) + { + designerTransaction.Commit(); + designerTransaction = null; + } + } + } + } + } + else //if commitedEditorNode == null and we are in commitEdit then just add the new Item, since this item is added through dropDown. + { + //if no editorNode then we are commiting a NEW NODE... + index = MenuItem.DropDownItems.IndexOf(typeHereNode); + try + { + dummyItemAdded = true; + CreateNewItem(type, index, text); + } + finally + { + dummyItemAdded = false; + } + // One place where we need to call this explicitly since the selection doesnt change. + typeHereNode.SelectControl(); + } + //Invalidate DropDown.. + MenuItem.DropDown.ResumeLayout(true); + MenuItem.DropDown.PerformLayout(); + //Reset the Glyphs after Item addition... + ResetGlyphs(MenuItem); + //set the selection to our new item only if item was commited via ENTER KEY in the Insitu Mode. + if (selSvc != null) + { + if (enterKeyPressed) + { + ToolStripItem nextItem; + if ((MenuItem.DropDownDirection == ToolStripDropDownDirection.AboveLeft || + MenuItem.DropDownDirection == ToolStripDropDownDirection.AboveRight) && index >= 1) + { + nextItem = MenuItem.DropDownItems[index - 1]; + } + else + { + nextItem = MenuItem.DropDownItems[index + 1]; + } + if (KeyboardHandlingService != null) + { + if (nextItem != null) + { + if (MenuItem.DropDownItems[index] is ToolStripDropDownItem currentItem) + { + currentItem.HideDropDown(); + } + } + if (nextItem == typeHereNode) + { + // Put Selection on the TypeHereNode. + KeyboardHandlingService.SelectedDesignerControl = nextItem; + selSvc.SetSelectedComponents(null, SelectionTypes.Replace); + } + else + { + KeyboardHandlingService.SelectedDesignerControl = null; + selSvc.SetSelectedComponents(new object[] { nextItem }); + } + } + } + else if (tabKeyPressed) //just select this the new Item + { + selSvc.SetSelectedComponents(new object[] { MenuItem.DropDownItems[index] }, SelectionTypes.Replace); + } + } + } + else //we come here if we have not committed so revert our state... + { + if (commitedEditorNode != null) /// we just changed some properties which we want to revert... + { + MenuItem.DropDown.SuspendLayout(); + bool dummyItem = dummyItemAdded; + dummyItemAdded = false; + int index = MenuItem.DropDownItems.IndexOf(commitedEditorNode); + ToolStripItem editedItem = (ToolStripItem)MenuItem.DropDownItems[index + 1]; + MenuItem.DropDown.Items.Remove(commitedEditorNode); + // put the item back... + editedItem.Visible = true; + if (commitedTemplateNode != null) + { + commitedTemplateNode.CloseEditor(); + commitedTemplateNode = null; + } + + if (commitedEditorNode != null) + { + commitedEditorNode.Dispose(); + commitedEditorNode = null; + } + if (dummyItem) + { + MenuItem.DropDownItems.Remove(editedItem); + try + { + designerHost.DestroyComponent(editedItem); + } + catch + { + if (newMenuItemTransaction != null) + { + try + { + newMenuItemTransaction.Cancel(); + } + catch //This will cause ROLLBACK and hence throw if the project is already in DEBUG and instuEdit was Active. + { + } + newMenuItemTransaction = null; + } + } + editedItem = null; + } + // Required to do here... + MenuItem.DropDown.ResumeLayout(); + //Remove the Glyphs so that Mouse Message go to the Editor + if (editedItem != null) + { + AddItemBodyGlyph(editedItem); + } + + if (dummyItem) + { + SelectionManager selMgr = (SelectionManager)GetService(typeof(SelectionManager)); + /// Since the operation is cancelled and there is no change of glyphs, Set SelectionManager.NeedRefresh to false so that it doesnt REFRESH, + /// when the transaction is cancelled. + selMgr.NeedRefresh = false; + if (newMenuItemTransaction != null) + { + try + { + dummyItemAdded = true; + newMenuItemTransaction.Cancel(); + newMenuItemTransaction = null; + if (MenuItem.DropDownItems.Count == 0) + { + CreatetypeHereNode(); + } + } + finally + { + dummyItemAdded = false; + } + } + dummyItem = false; + } + // Added for Separators... + MenuItem.DropDown.PerformLayout(); + } + } + } + + /// + /// Creates the dummy node for InSitu Edit. + /// + private void CreatetypeHereNode() + { + //Debug.Assert(typeHereNode == null, "Why is our dummy node valid?"); + if (typeHereNode == null) + { + AddNewTemplateNode(MenuItem.DropDown); + //Add Text for Debugging Non Sited DropDown.. + if (MenuItem.DropDown.Site == null) + { + MenuItem.DropDown.Text = MenuItem.Name + ".DropDown"; + } + } + else if (typeHereNode != null && MenuItem.DropDownItems.IndexOf(typeHereNode) == -1) + { + MenuItem.DropDown.Items.Add(typeHereNode); + typeHereNode.Visible = true; + } + //Invalidate DropDown.. + MenuItem.DropDown.PerformLayout(); + } + + /// + /// This Function is called by EnterInSituEdit where in we Swap the typeHereNode by the TemplateNode (the Insitu Editor). Since the TemplateNode had a EditorToolStrip we can just HOST that ToolStrip as a ToolStripContorlHost and add it to the DropDown. + /// + private void CreateDummyMenuItem(ToolStripItem item, string text, Image image) + { + commitedTemplateNode = new ToolStripTemplateNode(Component, text, image) + { + ActiveItem = item + }; + int width = commitedTemplateNode.EditorToolStrip.Width; + commitedEditorNode = new DesignerToolStripControlHost(commitedTemplateNode.EditorToolStrip) + { + AutoSize = false, + Width = width + }; + } + + /// + /// This helper function creates a dummyItem for Insitu editing. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + private ToolStripItem CreateDummyItem(Type t, int dummyIndex) + { + if (designerHost == null) + { + Debug.Fail("Couldn't get designer host!"); + return null; + } + + ToolStripItem newItem = null; + // For the "Upward DropDown" add at index +1... + if (MenuItem.DropDownDirection == ToolStripDropDownDirection.AboveLeft || MenuItem.DropDownDirection == ToolStripDropDownDirection.AboveRight) + { + dummyIndex++; + } + try + { + // turn off Adding/Added events listened to by the ToolStripDesigner... + ToolStripDesigner.s_autoAddNewItems = false; + + //Store the index into a class level member so that the componentChanged can access it + indexToInsertNewItem = dummyIndex; + + try + { + //create our transaction + if (newMenuItemTransaction == null) + { + newMenuItemTransaction = designerHost.CreateTransaction(SR.ToolStripCreatingNewItemTransaction); + } + fireComponentChanged = true; + newItem = (ToolStripItem)designerHost.CreateComponent(t); + } + finally + { + fireComponentChanged = false; + } + ToolStripItemDesigner designer = designerHost.GetDesigner(newItem) as ToolStripItemDesigner; + try + { + // ToolStripItem designer tries to set the TEXT for the item in the InitializeNewComponent(). But since we are create item thru InSitu .. we shouldnt do this. + designer.InternalCreate = true; + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + } + finally + { + designer.InternalCreate = false; + } + } + catch (InvalidOperationException ex) + { + CommitInsertTransaction(/*commit=*/false); + + if (newMenuItemTransaction != null) + { + newMenuItemTransaction.Cancel(); + newMenuItemTransaction = null; + } + IUIService uiService = (IUIService)GetService(typeof(IUIService)); + uiService.ShowError(ex.Message); + } + finally + { + // turn on Adding/Added events listened to by the ToolStripDesigner... + ToolStripDesigner.s_autoAddNewItems = true; + // Reset the index + indexToInsertNewItem = -1; + } + return newItem; + } + + /// + /// Asks the host to create a new DropDownItem, inserts the item into the collection & selects it. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + private ToolStripItem CreateNewItem(Type t, int dummyIndex, string newText) + { + if (designerHost == null) + { + Debug.Fail("Couldn't get designer host!"); + return null; + } + ToolStripItem newItem = null; + // For the "Upward DropDown" add at index +1... + if (MenuItem.DropDownDirection == ToolStripDropDownDirection.AboveLeft || MenuItem.DropDownDirection == ToolStripDropDownDirection.AboveRight) + { + dummyIndex++; + } + //create our transaction + DesignerTransaction outerTransaction = designerHost.CreateTransaction(SR.ToolStripCreatingNewItemTransaction); + try + { + // turn off Adding/Added events listened to by the ToolStripDesigner... + ToolStripDesigner.s_autoAddNewItems = false; + //Store the index into a class level member so that the componentChanged can access it + indexToInsertNewItem = dummyIndex; + try + { + fireComponentChanged = true; + newItem = (ToolStripItem)designerHost.CreateComponent(t, ToolStripDesigner.NameFromText(newText, t, MenuItem.Site)); + } + finally + { + fireComponentChanged = false; + } + ToolStripItemDesigner designer = designerHost.GetDesigner(newItem) as ToolStripItemDesigner; + try + { + // ToolStripItem designer tries to set the TEXT for the item in the InitializeNewComponent(). But since we are create item thru InSitu .. we shouldnt do this. + if (!string.IsNullOrEmpty(newText) || addingDummyItem) + { + designer.InternalCreate = true; + } + if (designer is ComponentDesigner) + { + ((ComponentDesigner)designer).InitializeNewComponent(null); + } + } + finally + { + designer.InternalCreate = false; + } + + //Set the Text and Image.. + if (newItem != null) + { + PropertyDescriptor textProperty = TypeDescriptor.GetProperties(newItem)["Text"]; + Debug.Assert(textProperty != null, "Could not find 'Text' property in ToolStripItem."); + if (textProperty != null && !string.IsNullOrEmpty(newText)) + { + textProperty.SetValue(newItem, newText); + } + } + } + catch + { + //There might be scenarios where the ComponentAdding is fired but the Added doesnt get fired. Is such cases the InsertTransaction might be still active... So we need to cancel that too here. + CommitInsertTransaction(false); + if (outerTransaction != null) + { + outerTransaction.Cancel(); + outerTransaction = null; + } + } + finally + { + if (outerTransaction != null) + { + outerTransaction.Commit(); + outerTransaction = null; + } + + // turn on Adding/Added events listened to by the ToolStripDesigner... + ToolStripDesigner.s_autoAddNewItems = true; + // Reset the index + indexToInsertNewItem = -1; + } + return newItem; + } + + /// + /// Helper function to find whether the passed in DropDownItems have same owner. + /// + private bool CheckSameOwner(ToolStripDropDownItem lastSelected, ToolStripDropDownItem currentSelected) + { + if (lastSelected != null && currentSelected != null) + { + if (lastSelected.Owner is ToolStripDropDown && currentSelected.Owner is ToolStripDropDown) + { + ToolStripItem ownerLastSelected = ((ToolStripDropDown)(lastSelected.Owner)).OwnerItem; + ToolStripItem ownerCurrentSelected = ((ToolStripDropDown)(currentSelected.Owner)).OwnerItem; + return (ownerLastSelected == ownerCurrentSelected); + } + } + return false; + } + + // internal method to commit any node. + internal void Commit() + { + if (commitedTemplateNode != null && commitedTemplateNode.Active) + { + //Get Index of the CommitedItem.. + int index = MenuItem.DropDownItems.IndexOf(commitedEditorNode); + commitedTemplateNode.Commit(false, false); + if (index != -1 && MenuItem.DropDownItems.Count > index) + { + if (MenuItem.DropDownItems[index] is ToolStripDropDownItem newItem) + { + newItem.HideDropDown(); + } + } + } + else if (typeHereTemplateNode != null && typeHereTemplateNode.Active) + { + typeHereTemplateNode.Commit(false, false); + } + // COMMIT ALL THE THE PARENT CHAIN .... + ToolStripDropDownItem currentItem = MenuItem; + while (currentItem != null && currentItem.Owner is ToolStripDropDown) + { + currentItem = (ToolStripDropDownItem)((ToolStripDropDown)(currentItem.Owner)).OwnerItem; + if (currentItem != null) + { + ToolStripMenuItemDesigner itemDesigner = (ToolStripMenuItemDesigner)designerHost.GetDesigner(currentItem); + if (itemDesigner != null) + { + itemDesigner.Commit(); + } + } + } + } + + /// + /// Disposes of this designer. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + //clean up + if (selSvc != null) + { + selSvc.SelectionChanged -= new EventHandler(this.OnSelectionChanged); + } + if (undoEngine != null) + { + undoEngine.Undoing -= new EventHandler(this.OnUndoing); + undoEngine.Undone -= new EventHandler(this.OnUndone); + } + if (MenuItem != null && MenuItem.HasDropDown) + { + MenuItem.DropDown.Hide(); + //Unhook the events... + UnHookEvents(); + } + + if (toolStripAdornerWindowService != null) + { + toolStripAdornerWindowService = null; + } + + // unhook notifications. + IComponentChangeService componentChangeSvc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (componentChangeSvc != null) + { + componentChangeSvc.ComponentRemoved -= new ComponentEventHandler(ComponentChangeSvc_ComponentRemoved); + componentChangeSvc.ComponentRemoving -= new ComponentEventHandler(ComponentChangeSvc_ComponentRemoving); + componentChangeSvc.ComponentAdding -= new ComponentEventHandler(ComponentChangeSvc_ComponentAdding); + componentChangeSvc.ComponentAdded -= new ComponentEventHandler(ComponentChangeSvc_ComponentAdded); + } + + if (typeHereTemplateNode != null) + { + typeHereTemplateNode.RollBack(); + typeHereTemplateNode.CloseEditor(); + typeHereTemplateNode = null; + } + + if (typeHereNode != null) + { + typeHereNode.Dispose(); + typeHereNode = null; + } + if (commitedTemplateNode != null) + { + commitedTemplateNode.RollBack(); + commitedTemplateNode.CloseEditor(); + commitedTemplateNode = null; + } + + if (commitedEditorNode != null) + { + commitedEditorNode.Dispose(); + commitedEditorNode = null; + } + + if (parentItem != null) + { + parentItem = null; + } + } + base.Dispose(disposing); + } + + // When the dropDown is clicked; Commit any active insitu node. + private void DropDownClick(object sender, EventArgs e) + { + // Commit any InsituEdit Node. + if (KeyboardHandlingService != null && KeyboardHandlingService.TemplateNodeActive) + { + // If templateNode Active .. commit and Select it + KeyboardHandlingService.ActiveTemplateNode.CommitAndSelect(); + } + } + + // re-paint required to "validate" the glyphs + private void DropDownPaint(object sender, PaintEventArgs e) + { + //Select All requires the repaint of glyphs after the DropDown receives paint message. + if (selSvc != null && MenuItem != null) + { + foreach (ToolStripItem item in MenuItem.DropDownItems) + { + if (item.Visible && selSvc.GetComponentSelected(item)) + { + if (designerHost.GetDesigner(item) is ToolStripItemDesigner designer) + { + Rectangle r = designer.GetGlyphBounds(); + ToolStripDesignerUtils.GetAdjustedBounds(item, ref r); + r.Inflate(GLYPHINSET, GLYPHINSET); + //this will allow any Glyphs to re-paint + //after this control and its designer has painted + BehaviorService b = (BehaviorService)GetService(typeof(BehaviorService)); + if (b != null) + { + b.ProcessPaintMessage(r); + } + } + } + // When you alt-tab from VS to any other application and come back and if the dropDown is open; then the templateNode doesnt paint. This happens when you have another control below the dropDown which paints over the controlhost so we need to refresh the TemplateNode in this case. + if (item is DesignerToolStripControlHost controlHost) + { + controlHost.Control.Refresh(); + } + } + } + } + + // Invalidate the BehaviorService if the location changed. + private void DropDownLocationChanged(object sender, EventArgs e) + { + // this shoulnt get fired manytimes.. only in certain case... but in those cases its needed to REFRESH THE ENTIRE adornerWindow. + ToolStripDropDown dropDown = sender as ToolStripDropDown; + if (dropDown.Visible) + { + BehaviorService behaviorService = (BehaviorService)GetService(typeof(BehaviorService)); + if (behaviorService != null) + { + behaviorService.Invalidate(); + } + } + } + + // Change the parent when the DropDown is opening. + private void DropDownItem_DropDownOpening(object sender, EventArgs e) + { + ToolStripDropDownItem ddi = sender as ToolStripDropDownItem; + if (toolStripAdornerWindowService != null) + { + ddi.DropDown.TopLevel = false; + ddi.DropDown.Parent = toolStripAdornerWindowService.ToolStripAdornerWindowControl; + } + } + + // Add the DropDownGlyph when the dropDown is opened. + private void DropDownItem_DropDownOpened(object sender, EventArgs e) + { + //Add Glyphs... + ToolStripDropDownItem ddi = sender as ToolStripDropDownItem; + if (ddi != null) + { + ResetGlyphs(ddi); + } + + // finally add Glyph for the "DropDown" + Control rootControl = ddi.DropDown; + if (rootControl != null) + { + if (designerHost.GetDesigner(designerHost.RootComponent) is ControlDesigner designer) + { + rootControlGlyph = new ToolStripDropDownGlyph(rootControl.Bounds, new DropDownBehavior(designer, this)); + } + if (toolStripAdornerWindowService != null) + { + toolStripAdornerWindowService.DropDownAdorner.Glyphs.Add(rootControlGlyph); + } + } + } + + // Remove the dropDownGlyph after the dropDown is closed. + private void DropDownItem_DropDownClosed(object sender, EventArgs e) + { + if (sender is ToolStripDropDownItem ddi) + { + //Invaliate the ToolStripWindow.... for clearing the dropDowns + if (toolStripAdornerWindowService != null) + { + if (rootControlGlyph != null && toolStripAdornerWindowService.DropDownAdorner.Glyphs.Contains(rootControlGlyph)) + { + toolStripAdornerWindowService.DropDownAdorner.Glyphs.Remove(rootControlGlyph); + } + } + //Remove body Glyphs... + InitializeBodyGlyphsForItems(false /*remove*/, ddi); + //Unhook all the Events + initialized = false; + UnHookEvents(); + // Check if this is a Sited-DropDown + if (ddi.DropDown.Site != null || ddi.DropDownItems.Count == 1) + { + //Get Designer ... and call Remove on that Designer. + RemoveTypeHereNode(ddi); + } + else if (toolStripAdornerWindowService != null) + { + toolStripAdornerWindowService.Invalidate(ddi.DropDown.Bounds); + } + } + } + + // invalidate the AdornerWindow when the DropDown resizes + private void DropDownResize(object sender, EventArgs e) + { + ToolStripDropDown dropDown = sender as ToolStripDropDown; + if (!dummyItemAdded) + { + if (dropDown != null && dropDown.Visible) + { + //Invalidate only if new Size is LESS than old Size... + if (toolStripAdornerWindowService != null && (dropDown.Width < dropDownSizeToInvalidate.Width || dropDown.Size.Height < dropDownSizeToInvalidate.Height)) + { + using (Region invalidatingRegion = new Region(dropDownSizeToInvalidate)) + { + invalidatingRegion.Exclude(dropDown.Bounds); + toolStripAdornerWindowService.Invalidate(invalidatingRegion); + //Invalidate BehaviorService AdornerWindow as well... but only the DropDownBounds + BehaviorService b = (BehaviorService)GetService(typeof(BehaviorService)); + if (b != null) + { + b.Invalidate(invalidatingRegion); + } + } + } + } + if (toolStripAdornerWindowService != null) + { + if (rootControlGlyph != null && toolStripAdornerWindowService.DropDownAdorner.Glyphs.Contains(rootControlGlyph)) + { + toolStripAdornerWindowService.DropDownAdorner.Glyphs.Remove(rootControlGlyph); + } + if (designerHost.GetDesigner(designerHost.RootComponent) is ControlDesigner designer) + { + rootControlGlyph = new ToolStripDropDownGlyph(dropDown.Bounds, new DropDownBehavior(designer, this)); + } + toolStripAdornerWindowService.DropDownAdorner.Glyphs.Add(rootControlGlyph); + } + } + dropDownSizeToInvalidate = dropDown.Bounds; + } + + /// + /// Called when a menuItem wants to go into InSitu Editing Mode. + /// + internal void EditTemplateNode(bool clicked) + { + // If the parent has a window which is too small, there won't be any space to draw the entry box and typeHereNode will be null + if (typeHereNode == null) + { + return; + } + + // Refresh the state of the 'TypeHere' node to NotSelected state + typeHereNode.RefreshSelectionGlyph(); + // Commit any InsituEdit Node. + if (KeyboardHandlingService != null && KeyboardHandlingService.TemplateNodeActive) + { + // If templateNode Active .. commit and Select it + KeyboardHandlingService.ActiveTemplateNode.CommitAndSelect(); + } + + // commit any existing insitu editor... + if (clicked) + { + // We should come here for a valid parent !!! + if (MenuItem == null) + { + return; + } + } + + // always select the parent item... but dont redraw the control during this Selection Change as this causes flicker + try + { + ToolStripDesigner.s_editTemplateNode = true; + selSvc.SetSelectedComponents(new object[] { MenuItem }, SelectionTypes.Replace); + } + finally + { + ToolStripDesigner.s_editTemplateNode = false; + } + + // Hide the DropDown of any previous Selection.. First get the SelectedItem... + ToolStripDropDownItem selectedItem = null; + if (selSvc.PrimarySelection == null && KeyboardHandlingService != null) + { + if (KeyboardHandlingService.SelectedDesignerControl is ToolStripItem item) + { + selectedItem = ((ToolStripDropDown)item.Owner).OwnerItem as ToolStripDropDownItem; + } + } + else + { + selectedItem = selSvc.PrimarySelection as ToolStripDropDownItem; + } + + // Now Hide the DropDown and Refresh Glyphs... + if (selectedItem != null && selectedItem != MenuItem) + { + HideSiblingDropDowns(selectedItem); + } + + MenuItem.DropDown.SuspendLayout(); + dummyItemAdded = true; + int index = MenuItem.DropDownItems.IndexOf(typeHereNode); + ToolStripItem newDummyItem = null; + try + { + addingDummyItem = true; + newDummyItem = CreateDummyItem(typeof(ToolStripMenuItem), index); + } + catch (CheckoutException checkoutException) + { + if (checkoutException.Equals(CheckoutException.Canceled)) + { + CommitInsertTransaction(/*commit=*/ false); + + if (newMenuItemTransaction != null) + { + newMenuItemTransaction.Cancel(); + newMenuItemTransaction = null; + } + } + else + { + throw; + } + } + finally + { + dummyItemAdded = (newDummyItem != null); + addingDummyItem = false; + } + + MenuItem.DropDown.ResumeLayout(); + if (newDummyItem != null) + { + if (designerHost.GetDesigner(newDummyItem) is ToolStripMenuItemDesigner newItemDesigner) + { + newItemDesigner.InitializeDropDown(); + newItemDesigner.ShowEditNode(clicked); + } + } + } + + /// + /// Called from OnDoubleClickTimerTick to Enter in InsituMode + /// + private void EnterInSituMode() + { + //we need to tell our parent that we want to enter insitu edit mode + if (MenuItem.Owner is ToolStripDropDown) + { + ToolStripItem ownerItem = ((ToolStripDropDown)(MenuItem.Owner)).OwnerItem; + //need to inform the owner tha we want to enter insitu mode + if (designerHost != null) + { + IDesigner designer = designerHost.GetDesigner(ownerItem); + if (designer is ToolStripMenuItemDesigner) + { + // Need to Add Dummy Node For Direct Insitu.. + MenuItem.HideDropDown(); + ((ToolStripMenuItemDesigner)designer).EnterInSituEdit(MenuItem); + } + } + } + } + + /// + /// This method replaces the menItem with an in-situ TemplateNode. + /// + internal void EnterInSituEdit(ToolStripItem toolItem) + { + MenuItem.DropDown.SuspendLayout(); + //Remove the Glyphs so that Mouse Message go to the Editor + RemoveItemBodyGlyph(toolItem); + + if (toolItem == null) + { + return; + } + // we can only one Active Editor at one time. + if (IsEditorActive) + { + return; + } + + CreateDummyMenuItem(toolItem, toolItem.Text, toolItem.Image); + int index = MenuItem.DropDownItems.IndexOf(toolItem); + //swap in our insitu ToolStrip + MenuItem.DropDownItems.Insert(index, commitedEditorNode); + if (toolItem is ToolStripControlHost) + { + ((ToolStripControlHost)toolItem).Control.Visible = false; + } + toolItem.Visible = false; + MenuItem.DropDown.ResumeLayout(); + // Try Focusing the TextBox.... + if (commitedTemplateNode != null) + { + commitedTemplateNode.FocusEditor(toolItem); + } + ToolStripDropDownItem dropDownItem = toolItem as ToolStripDropDownItem; + if (!(dropDownItem.Owner is ToolStripDropDownMenu) && dropDownItem != null && dropDownItem.Bounds.Width < commitedEditorNode.Bounds.Width) + { + dropDownItem.Width = commitedEditorNode.Width; + dropDownItem.DropDown.Location = new Point(dropDownItem.DropDown.Location.X + commitedEditorNode.Bounds.Width - dropDownItem.Bounds.Width, dropDownItem.DropDown.Location.Y); + } + IsEditorActive = true; + } + + /// + /// Get the Insertion Index to drop the current drag-drop item. + /// + /// Returns the DropDown for this MenuItem else returns the Parent(Owner). + /// + protected override Component GetOwnerForActionList() + { + return MenuItem; + } + + // Helper Function to get the Main ToolStrip (MenuStrip); + internal override ToolStrip GetMainToolStrip() + { + ToolStripDropDown topmost = GetFirstDropDown(MenuItem); + ToolStripItem topMostItem = (topmost == null) ? null : topmost.OwnerItem; + if (topMostItem != null) + { + return topMostItem.Owner; + } + return MenuItem.Owner; + } + + /// + /// Helper function to Hide the Active Dropdown from the given DropDownItem. + /// + // Standard 'catch all - rethrow critical' exception pattern + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + private void HideAllDropDowns(ToolStripDropDownItem item) + { + try + { + if (MenuItem.Owner is ToolStripDropDown) + { + ToolStripItem ownerItem = ((ToolStripDropDown)(MenuItem.Owner)).OwnerItem; + while (item != ownerItem) + { + if (item.DropDown.Visible) + { + item.HideDropDown(); + } + if (item.Owner is ToolStripDropDown) + { + item = (ToolStripDropDownItem)((ToolStripDropDown)(item.Owner)).OwnerItem; + } + else + { + return; + } + } + } + } + catch (Exception e) + { + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + } + } + + /// + /// Helper function to Hide the Active Dropdown for all the siblings of the given DropDownItem. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + private void HideSiblingDropDowns(ToolStripDropDownItem item) + { + try + { + ToolStripItem ownerItem = MenuItem; + while (item != ownerItem) + { + item.HideDropDown(); + if (item.Owner is ToolStripDropDown) + { + item = (ToolStripDropDownItem)((ToolStripDropDown)(item.Owner)).OwnerItem; + } + else + { + return; + } + } + } + catch (Exception e) + { + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + } + } + + /// + /// This will listen to the necessary dropDown events... now we add the events on selection and unhook when the dropDown is closed. + /// + internal void HookEvents() + { + if (MenuItem != null) + { + MenuItem.DropDown.Closing += new ToolStripDropDownClosingEventHandler(OnDropDownClosing); + MenuItem.DropDownOpening += new EventHandler(DropDownItem_DropDownOpening); + MenuItem.DropDownOpened += new EventHandler(DropDownItem_DropDownOpened); + MenuItem.DropDownClosed += new EventHandler(DropDownItem_DropDownClosed); + MenuItem.DropDown.Resize += new System.EventHandler(this.DropDownResize); + MenuItem.DropDown.ItemAdded += new ToolStripItemEventHandler(OnItemAdded); + MenuItem.DropDown.Paint += new PaintEventHandler(this.DropDownPaint); + MenuItem.DropDown.Click += new EventHandler(this.DropDownClick); + MenuItem.DropDown.LocationChanged += new EventHandler(this.DropDownLocationChanged); + } + } + + /// + /// Initializes the ToolStripDropDownItem Designer. + /// + public override void Initialize(IComponent component) + { + base.Initialize(component); + // initialize the properties we will be shadowing + Visible = true; + DoubleClickEnabled = MenuItem.DoubleClickEnabled; + //hook our services + selSvc = (ISelectionService)GetService(typeof(ISelectionService)); + if (selSvc != null) + { + selSvc.SelectionChanged += new EventHandler(this.OnSelectionChanged); + } + + //hookup to the AdornerService.. + toolStripAdornerWindowService = (ToolStripAdornerWindowService)GetService(typeof(ToolStripAdornerWindowService)); + designerHost = (IDesignerHost)GetService(typeof(IDesignerHost)); + //Set the DoubleClickEnabled + MenuItem.DoubleClickEnabled = true; + + // attach notifcations. + IComponentChangeService componentChangeSvc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + if (componentChangeSvc != null) + { + componentChangeSvc.ComponentRemoved += new ComponentEventHandler(ComponentChangeSvc_ComponentRemoved); + componentChangeSvc.ComponentRemoving += new ComponentEventHandler(ComponentChangeSvc_ComponentRemoving); + componentChangeSvc.ComponentAdding += new ComponentEventHandler(ComponentChangeSvc_ComponentAdding); + componentChangeSvc.ComponentAdded += new ComponentEventHandler(ComponentChangeSvc_ComponentAdded); + } + + if (undoEngine == null) + { + undoEngine = GetService(typeof(UndoEngine)) as UndoEngine; + if (undoEngine != null) + { + undoEngine.Undoing += new EventHandler(this.OnUndoing); + undoEngine.Undone += new EventHandler(this.OnUndone); + } + } + } + + // internal since the Behavior uses this when the Items are moved on the DropDown. + internal void InitializeBodyGlyphsForItems(bool addGlyphs /* true for add */, ToolStripDropDownItem item) + { + if (addGlyphs) + { + AddBodyGlyphs(item); + } + else + { + RemoveBodyGlyphs(item); + } + } + + /// + /// Important function that initializes the dropDown with the typeHereNode , hooks the events and then shows the dropDown. + /// + internal void InitializeDropDown() + { + ToolStrip main = GetMainToolStrip(); + // Check if the TopMostItem is on normal dropdown or on the overflow. + ToolStripDropDown firstDropDown = GetFirstDropDown(MenuItem); + if (firstDropDown != null) + { + ToolStripItem topMostItem = firstDropDown.OwnerItem; + if ((topMostItem != null && topMostItem.GetCurrentParent() is ToolStripOverflow) && !main.CanOverflow) + { + return; + } + } + + if (!initialized) + { + initialized = true; + // When the DropDown is Shared the ownerItem need not be the current MenuItem. In Such a case hide the dropDown for current owner ... this will bring everything to a sane state.. + if (MenuItem.DropDown.OwnerItem is ToolStripDropDownItem currentOwner && currentOwner != MenuItem) + { + if (designerHost.GetDesigner(currentOwner) is ToolStripMenuItemDesigner ownerdesigner) + { + ownerdesigner.RemoveTypeHereNode(currentOwner); + } + currentOwner.HideDropDown(); + } + + // Check if this is a Sited-DropDown + if (MenuItem.DropDown.Site != null) + { + ToolStripDropDownDesigner designer = designerHost.GetDesigner(MenuItem.DropDown) as ToolStripDropDownDesigner; + if (designer != null) + { + designer.currentParent = MenuItem as ToolStripMenuItem; + } + } + + CreatetypeHereNode(); + MenuItem.DropDown.TopLevel = false; + //Allow Drag - Drop.... + MenuItem.DropDown.AllowDrop = true; + HookEvents(); + MenuItem.DropDown.AutoClose = false; + MenuItem.ShowDropDown(); + ShowOwnerDropDown(MenuItem); + + //Everytime you intitalize the dropDown Reset Glyphs + ResetGlyphs(MenuItem); + + if (!IsOnContextMenu && !dummyItemAdded) + { + // Required to show the SelectionBorder when the item is selected through the PropertyGrid or Doc outline. + SelectionManager selMgr = (SelectionManager)GetService(typeof(SelectionManager)); + // used the cached value... + if (selMgr != null) + { + selMgr.Refresh(); + } + } + BehaviorService behaviorService = (BehaviorService)GetService(typeof(BehaviorService)); + if (behaviorService != null) + { + behaviorService.Invalidate(MenuItem.Owner.Bounds); + } + } + } + + private bool IsParentDropDown(ToolStripDropDown currentDropDown) + { + if (currentDropDown != null) + { + ToolStripDropDown startDropDown = MenuItem.Owner as ToolStripDropDown; + while (startDropDown != null && startDropDown != currentDropDown) + { + if (startDropDown.OwnerItem is ToolStripDropDownItem ownerItem) + { + startDropDown = ownerItem.Owner as ToolStripDropDown; + } + else + { + startDropDown = null; + } + } + if (startDropDown == null) + { + return false; + } + return true; + } + return false; + } + + /// + /// This will morph the current item to the provided type "t" of the item... + /// + internal override ToolStripItem MorphCurrentItem(Type t) + { + //Get the Hosting DropDown'd bounds + Rectangle hostingDropDownBounds = (MenuItem.GetCurrentParent()).Bounds; + //Get the currentItems DropDownBounds + Rectangle itemDropDownBounds = MenuItem.DropDown.Bounds; + //Remove body Glyphs... + InitializeBodyGlyphsForItems(false /*remove*/, MenuItem); + Rectangle boundstoInvalidate = Rectangle.Union(hostingDropDownBounds, itemDropDownBounds); + ToolStripAdornerWindowService toolStripservice = toolStripAdornerWindowService; + ToolStripItem newItem = base.MorphCurrentItem(t); + // We loose the ToolStripWindowService after Morphing... so use the cached one. + if (toolStripservice != null) + { + toolStripservice.Invalidate(boundstoInvalidate); + toolStripservice = null; + } + return newItem; + } + + /// + /// Fired after a component has been added. Here, we add it to the winbar and select it. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private void ComponentChangeSvc_ComponentAdded(object sender, ComponentEventArgs e) + { + IComponentChangeService changeSvc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + // make sure it's one of ours and on DropDown. + if (e.Component is ToolStripItem newItem && componentAddingFired && (MenuItemSelected || fireComponentChanged)) + { + componentAddingFired = false; + try + { + if (IsOnContextMenu && MenuItem.DropDown.Site != null) + { + if (changeSvc != null) + { + MemberDescriptor member = TypeDescriptor.GetProperties(MenuItem.DropDown)["Items"]; + changeSvc.OnComponentChanging(MenuItem.DropDown, member); + } + } + else + { + base.RaiseComponentChanging(TypeDescriptor.GetProperties(MenuItem)["DropDownItems"]); + } + + // Get the current count of ToolStripItems. + int count = MenuItem.DropDownItems.Count; + + // In Cut / Copy and Paste the 'indexToInsertNewItem' is not Set and hence is -1... so check for that value... + if (indexToInsertNewItem != -1) + { + if (IsOnContextMenu && MenuItem.DropDown.Site != null) + { + MenuItem.DropDown.Items.Insert(indexToInsertNewItem, newItem); + } + else + { + MenuItem.DropDownItems.Insert(indexToInsertNewItem, newItem); + } + } + else + { + // Insert Item at the current Selection... + if (selSvc.PrimarySelection is ToolStripItem selectedItem && selectedItem != MenuItem) + { + int index = MenuItem.DropDownItems.IndexOf(selectedItem); + if (MenuItem.DropDownDirection == ToolStripDropDownDirection.AboveLeft || MenuItem.DropDownDirection == ToolStripDropDownDirection.AboveRight) + { + // Add at the next Index in case of "Up-Directed" dropDown. + if (IsOnContextMenu && MenuItem.DropDown.Site != null) + { + MenuItem.DropDown.Items.Insert(index + 1, newItem); + } + else + { + MenuItem.DropDownItems.Insert(index + 1, newItem); + } + } + else + { + if (IsOnContextMenu && MenuItem.DropDown.Site != null) + { + MenuItem.DropDown.Items.Insert(index, newItem); + } + else + { + MenuItem.DropDownItems.Insert(index, newItem); + } + } + } + else + { + if (count > 0) + { + if (MenuItem.DropDownDirection != ToolStripDropDownDirection.AboveLeft && MenuItem.DropDownDirection != ToolStripDropDownDirection.AboveRight) + { + // ADD at Last but one, the last one being the TemplateNode always... + if (IsOnContextMenu && MenuItem.DropDown.Site != null) + { + MenuItem.DropDown.Items.Insert(count - 1, newItem); + } + else + { + MenuItem.DropDownItems.Insert(count - 1, newItem); + } + } + } + else //count == 0 + { + if (IsOnContextMenu && MenuItem.DropDown.Site != null) + { + MenuItem.DropDown.Items.Add(newItem); + } + else + { + MenuItem.DropDownItems.Add(newItem); + } + } + } + } + // If the Item is added through Undo/Redo ONLY then select the item + if (undoingCalled) + { + if (selSvc != null) + { + selSvc.SetSelectedComponents(new IComponent[] { newItem }, SelectionTypes.Replace); + } + } + ResetGlyphs(MenuItem); + } + catch + { + CommitInsertTransaction(/*commit=*/false); + } + finally + { + if (IsOnContextMenu && MenuItem.DropDown.Site != null) + { + if (changeSvc != null) + { + MemberDescriptor member = TypeDescriptor.GetProperties(MenuItem.DropDown)["Items"]; + changeSvc.OnComponentChanged(MenuItem.DropDown, member, null, null); + } + } + else + { + base.RaiseComponentChanged(TypeDescriptor.GetProperties(MenuItem)["DropDownItems"], null, null); + } + CommitInsertTransaction(/*commit=*/true); + } + } + } + + private void CommitInsertTransaction(bool commit) + { + if (!IsOnContextMenu) + { + ToolStrip mainStrip = GetMainToolStrip(); + if (designerHost.GetDesigner(mainStrip) is ToolStripDesigner mainStripDesigner && mainStripDesigner.InsertTansaction != null) + { + if (commit) + { + mainStripDesigner.InsertTansaction.Commit(); + } + else + { + mainStripDesigner.InsertTansaction.Cancel(); + } + mainStripDesigner.InsertTansaction = null; + } + } + else + { + if (insertMenuItemTransaction != null) + { + if (commit) + { + insertMenuItemTransaction.Commit(); + } + else + { + insertMenuItemTransaction.Cancel(); + } + insertMenuItemTransaction = null; + } + } + } + /// + /// Checks if the component being added is a child ToolStripItem. + /// + private void ComponentChangeSvc_ComponentAdding(object sender, ComponentEventArgs e) + { + //Dont do anything if CopyInProgress is true + if (KeyboardHandlingService != null && KeyboardHandlingService.CopyInProgress) + { + return; + } + if (e.Component is ToolStripItem && (MenuItemSelected || fireComponentChanged)) + { + if (!IsOnContextMenu) + { + ToolStrip mainStrip = GetMainToolStrip(); + if (designerHost.GetDesigner(mainStrip) is ToolStripDesigner mainStripDesigner && !mainStripDesigner.EditingCollection && mainStripDesigner.InsertTansaction == null) + { + componentAddingFired = true; + Debug.Assert(designerHost != null, "Why didn't we get a designer host?"); + mainStripDesigner.InsertTansaction = designerHost.CreateTransaction(SR.ToolStripInsertingIntoDropDownTransaction); + } + } + else //we are on ContextMenuStrip, ToolStripDropDown or ToolStripDropDownMenu.... + { + if (e.Component is ToolStripItem itemAdding && itemAdding.Owner == null) + { + componentAddingFired = true; + Debug.Assert(designerHost != null, "Why didn't we get a designer host?"); + insertMenuItemTransaction = designerHost.CreateTransaction(SR.ToolStripInsertingIntoDropDownTransaction); + } + } + } + } + + /// + /// After a ToolStripItem is removed, remove it from the ToolStrip and select the next item. + /// + private void ComponentChangeSvc_ComponentRemoved(object sender, ComponentEventArgs e) + { + if (e.Component is ToolStripItem itemToBeDeleted && itemToBeDeleted.IsOnDropDown) + { + if (itemToBeDeleted.Owner is ToolStripDropDown owner) + { + //Get the ownerItem + ToolStripDropDownItem ownerItem = (ToolStripDropDownItem)((ToolStripDropDown)(itemToBeDeleted.Owner)).OwnerItem; + if (ownerItem != null && ownerItem == MenuItem) + { + int itemIndex = ownerItem.DropDownItems.IndexOf(itemToBeDeleted); + // send notifications. + try + { + if (itemIndex != -1) + { + ownerItem.DropDownItems.Remove(itemToBeDeleted); + base.RaiseComponentChanged(TypeDescriptor.GetProperties(ownerItem)["DropDownItems"], null, null); + } + } + finally + { + if (_pendingTransaction != null) + { + _pendingTransaction.Commit(); + _pendingTransaction = null; + } + } + + //Remove & Add the Glyphs + ResetGlyphs(ownerItem); + // select the next item or the ToolStrip itself. + if (ownerItem.DropDownItems.Count > 1) + { + itemIndex = Math.Min(ownerItem.DropDownItems.Count - 1, itemIndex); + itemIndex = Math.Max(0, itemIndex); + } + else + { + itemIndex = -1; + } + + // Looks like we need to invalidate the entire + if (toolStripAdornerWindowService != null && boundsToInvalidateOnRemove != Rectangle.Empty) + { + using (Region regionToInvalidate = new Region(boundsToInvalidateOnRemove)) + { + regionToInvalidate.Exclude(MenuItem.DropDown.Bounds); + toolStripAdornerWindowService.Invalidate(regionToInvalidate); + boundsToInvalidateOnRemove = Rectangle.Empty; + } + } + + // Select the item only if Cut/Delete is pressed. + if (KeyboardHandlingService != null && KeyboardHandlingService.CutOrDeleteInProgress) + { + if (selSvc != null && !dummyItemAdded) + { + IComponent targetSelection = (itemIndex == -1) ? (IComponent)ownerItem : (IComponent)ownerItem.DropDownItems[itemIndex]; + // if the TemplateNode becomes the targetSelection, then set the targetSelection to null. + if (targetSelection is DesignerToolStripControlHost) + { + KeyboardHandlingService.SelectedDesignerControl = targetSelection; + KeyboardHandlingService.OwnerItemAfterCut = MenuItem; + selSvc.SetSelectedComponents(null, SelectionTypes.Replace); + } + else + { + selSvc.SetSelectedComponents(new IComponent[] { targetSelection }, SelectionTypes.Replace); + } + } + } + } + } + } + } + + /// + /// Before a ToolStripItem is removed, open a transaction to batch the operation. + /// + private void ComponentChangeSvc_ComponentRemoving(object sender, ComponentEventArgs e) + { + if (dummyItemAdded) + { + return; + } + if (e.Component is ToolStripItem itemToBeDeleted && itemToBeDeleted.IsOnDropDown && itemToBeDeleted.Placement == ToolStripItemPlacement.Main) + { + if (itemToBeDeleted.Owner is ToolStripDropDown owner) + { + //Get the ownerItem + ToolStripDropDownItem ownerItem = (ToolStripDropDownItem)((ToolStripDropDown)(itemToBeDeleted.Owner)).OwnerItem; + if (ownerItem != null && ownerItem == MenuItem) + { + RemoveItemBodyGlyph(itemToBeDeleted); + InitializeBodyGlyphsForItems(false, ownerItem); + boundsToInvalidateOnRemove = ownerItem.DropDown.Bounds; + //Check if the deleted item is a dropDownItem and its DropDown is Visible. + if (itemToBeDeleted is ToolStripDropDownItem dropDownItem) + { + boundsToInvalidateOnRemove = Rectangle.Union(boundsToInvalidateOnRemove, dropDownItem.DropDown.Bounds); + } + Debug.Assert(designerHost != null, "Why didn't we get a designer host?"); + Debug.Assert(_pendingTransaction == null, "Adding item with pending transaction?"); + try + { + _pendingTransaction = designerHost.CreateTransaction(SR.ToolStripDesignerTransactionRemovingItem); + base.RaiseComponentChanging(TypeDescriptor.GetProperties(ownerItem)["DropDownItems"]); + } + catch + { + if (_pendingTransaction != null) + { + _pendingTransaction.Cancel(); + _pendingTransaction = null; + } + } + } + } + } + } + + /// + /// Controls the dismissal of the drop down, here - we just cancel it + /// + private void OnDropDownClosing(object sender, ToolStripDropDownClosingEventArgs e) + { + //always dismiss this so we don't collapse the dropdown when the user clicks @ design time + e.Cancel = (e.CloseReason == ToolStripDropDownCloseReason.ItemClicked); + } + + /// + /// When DropDown is disposed; nullify the dropDown. + /// + private void OnDropDownDisposed(object sender, EventArgs e) + { + if (MenuItem != null) + { + if (MenuItem.DropDown != null) + { + MenuItem.DropDown.Disposed -= new EventHandler(OnDropDownDisposed); + } + // This is necessary when the MenuItem's DropDown property is SET to something other than the default DropDown. + MenuItem.DropDown = null; + } + } + + /// + /// When a item is added, re-arrange the elements to make sure that the templateNode is at the end.. + /// + private void OnItemAdded(object sender, ToolStripItemEventArgs e) + { + // Reshuffle the TemplateNode only for "Downward" dropdowns... + if (MenuItem.DropDownDirection != ToolStripDropDownDirection.AboveLeft && MenuItem.DropDownDirection != ToolStripDropDownDirection.AboveRight) + { + if (typeHereNode != null && (e.Item != typeHereNode)) + { + int currentIndexOfEditor = MenuItem.DropDown.Items.IndexOf(typeHereNode); + if (currentIndexOfEditor >= 0 && currentIndexOfEditor < MenuItem.DropDown.Items.Count - 1) + { + // we now know the editor is there, but isnt currently at the end of the line. lets add it. + MenuItem.DropDown.ItemAdded -= new ToolStripItemEventHandler(OnItemAdded); + MenuItem.DropDown.SuspendLayout(); + MenuItem.DropDown.Items.Remove(typeHereNode); + MenuItem.DropDown.Items.Add(typeHereNode); + MenuItem.DropDown.ResumeLayout(); + MenuItem.DropDown.ItemAdded += new ToolStripItemEventHandler(OnItemAdded); + } + else + { + CreatetypeHereNode(); + } + } + } + } + + /// + /// Called during Undo (this is used for DropDown Property) + /// + private void OnUndone(object source, EventArgs e) + { + if (undoingCalled) + { + // If we have Undone the SETTING of DropDown Revert back to Original state. + if (dropDownSet && MenuItem.DropDown.IsAutoGenerated) + { + ToolStrip mainStrip = GetMainToolStrip(); + if (designerHost.GetDesigner(mainStrip) is ToolStripDesigner mainStripDesigner && mainStripDesigner.CacheItems) + { + foreach (ToolStripItem item in mainStripDesigner.Items) + { + MenuItem.DropDownItems.Insert(0, item); + } + mainStripDesigner.CacheItems = false; + } + ResetGlyphs(MenuItem); + } + + // since the dropDown is closed during UnDoing .. we need to re-open the dropDown in Undone. + if (MenuItem != null && selSvc.GetComponentSelected(MenuItem)) + { + InitializeDropDown(); + MenuItem.DropDown.PerformLayout(); + } + undoingCalled = false; + dropDownSet = false; + } + // After Redo-Undo Glyphs are broken. + if (selSvc.GetComponentSelected(MenuItem) && !dropDownSetFailed) + { + InitializeDropDown(); + } + } + + /// + /// Called during Undo (this is used for DropDown Property) + /// + private void OnUndoing(object source, EventArgs e) + { + if (dummyItemAdded) + { + return; + } + if (!IsOnContextMenu && MenuItem.DropDown.Visible) + { + MenuItem.HideDropDown(); + + if (!MenuItem.DropDown.IsAutoGenerated) + { + dropDownSet = true; + ToolStrip mainStrip = GetMainToolStrip(); + if (designerHost.GetDesigner(mainStrip) is ToolStripDesigner mainStripDesigner) + { + mainStripDesigner.CacheItems = true; + mainStripDesigner.Items.Clear(); + } + } + undoingCalled = true; + } + } + + /// + /// Once a menuitem designer has selection - be sure to expand and collapse all necessary child/parent items Implements the Selection Paint Logic by adding Text to Tag property. Also Hides Unnecessary DropDowns. + /// + private void OnSelectionChanged(object sender, EventArgs e) + { + //determine if we are selected + if (MenuItem == null) + { + return; + } + + ISelectionService selectionSvc = sender as ISelectionService; + Debug.Assert(selectionSvc != null, "No Selection Service !!"); + if (selectionSvc == null) + { + return; + } + + //ALWAYS COMMIT!!! + if (commitedTemplateNode != null && commitedTemplateNode.Active) + { + commitedTemplateNode.Commit(false, false); + } + else if (typeHereTemplateNode != null && typeHereTemplateNode.Active) + { + typeHereTemplateNode.Commit(false, false); + } + if (MenuItem.Equals(selectionSvc.PrimarySelection)) + { + ArrayList origSel = ToolStripDesignerUtils.originalSelComps; + if (origSel != null) + { + ToolStripDesignerUtils.InvalidateSelection(origSel, MenuItem, MenuItem.Site, false /*shift pressed*/); + } + if (IsOnContextMenu && !MenuItem.Owner.Visible) + { + ToolStripDropDown firstDropDown = GetFirstDropDown(MenuItem); + ToolStripDropDownDesigner firstDropDownDesigner = designerHost.GetDesigner(firstDropDown) as ToolStripDropDownDesigner; + if (firstDropDownDesigner != null) + { + InitializeDropDown(); + firstDropDownDesigner.ShowMenu(); + firstDropDownDesigner.AddSelectionGlyphs(); + } + } + else + { + InitializeDropDown(); + } + + //Cache original selection + ICollection originalSelComps = null; + if (selSvc != null) + { + originalSelComps = selectionSvc.GetSelectedComponents(); + } + + // Add the TemplateNode to the Selection if it is currently Selected as the GetSelectedComponents wont do it for us. + origSel = new ArrayList(originalSelComps); + if (origSel.Count == 0) + { + if (KeyboardHandlingService != null && KeyboardHandlingService.SelectedDesignerControl != null) + { + origSel.Add(KeyboardHandlingService.SelectedDesignerControl); + } + } + + if (origSel.Count > 0) + { + ToolStripDesignerUtils.originalSelComps = origSel; + } + } + else + { + object selectedObj = ((ISelectionService)sender).PrimarySelection; + if (selectedObj == null) + { + if (KeyboardHandlingService != null) + { + selectedObj = KeyboardHandlingService.SelectedDesignerControl; + } + } + if (selectedObj is ToolStripItem currentSelection) + { + ToolStripDropDown parent = currentSelection.Owner as ToolStripDropDown; + while (parent != null) + { + if (parent.OwnerItem == MenuItem || parent.OwnerItem == null) + { + return; + } + else + { + parent = ((ToolStripItem)(parent.OwnerItem)).Owner as ToolStripDropDown; + } + } + } + if (MenuItem.DropDown.Visible) + { + // CASE : Check if the DropDown Selected is the one assigned to this MenuItem's DropDown property. If MenuItem.DropDown matches the currentSelection or is the First Dropdown of any selection THEN return + if (selectedObj is ToolStripDropDown selectedDropDown && MenuItem.DropDown == selectedDropDown) + { + return; + } + else + { + // Any ToolStripItem on the DropDown OR any of its Child DropDowns + ToolStripItem item = selectedObj as ToolStripItem; + if (item != null) + { + ToolStripDropDown parent = item.Owner as ToolStripDropDown; + while (parent != null) + { + if (parent == MenuItem.DropDown) + { + return; + } + else + { + parent = ((ToolStripItem)(parent.OwnerItem)).Owner as ToolStripDropDown; + } + } + } + } + // Close your own dropDown... + if (MenuItem.DropDown.OwnerItem == MenuItem) + { + MenuItem.HideDropDown(); + } + } + } + } + + /// + /// Allows a designer to filter the set of properties the component it is designing will expose through the TypeDescriptor object. This method is called immediately before its corresponding "Post" method. If you are overriding this method you should call the base implementation before you perform your own filtering. + /// + protected override void PreFilterProperties(IDictionary properties) + { + base.PreFilterProperties(properties); + // Handle shadowed properties + string[] shadowProps = new string[] { "Visible", "DoubleClickEnabled", "CheckOnClick", "DropDown" }; + PropertyDescriptor prop; + Attribute[] empty = new Attribute[0]; + for (int i = 0; i < shadowProps.Length; i++) + { + prop = (PropertyDescriptor)properties[shadowProps[i]]; + if (prop != null) + { + properties[shadowProps[i]] = TypeDescriptor.CreateProperty(typeof(ToolStripMenuItemDesigner), prop, empty); + } + } + } + + // + // Resets the ToolStripMenuItem DoubleClickEnabled to be the default visible + // + private void ResetDoubleClickEnabled() => DoubleClickEnabled = false; + + // + // Resets the ToolStripMenuItem CheckOnClick to be the default visible + // + private void ResetCheckOnClick() => CheckOnClick = false; + + // + // Resets the ToolStripMenuItem CheckOnClick to be the default visible + // + private void ResetDropDown() => DropDown = null; + + // + // Resets the ToolStripMenuItem Visible to be the default visible + // + private void ResetVisible() => Visible = true; + + // + // Restores the ToolStripMenuItem Visible to be the value set in the property grid. + // + private void RestoreVisible() => MenuItem.Visible = Visible; + + // + // Removes the TypeHere node when the DropDownCloses. + // + internal void RemoveTypeHereNode(ToolStripDropDownItem ownerItem) + { + //This will cause the DropDown to Relayout so that during the DropDownClosed event we wont have proper Bounds to Invalidate the ToolStripAdorner... So for this case do it here... + Rectangle bounds = ownerItem.DropDown.Bounds; + if (ownerItem.DropDownItems.Count > 0 && ownerItem.DropDownItems[0] is DesignerToolStripControlHost) + { + ownerItem.DropDownItems.RemoveAt(0); + } + if (typeHereTemplateNode != null && typeHereTemplateNode.Active) + { + typeHereTemplateNode.RollBack(); + typeHereTemplateNode.CloseEditor(); + typeHereTemplateNode = null; + } + if (typeHereNode != null) + { + typeHereNode.Dispose(); + typeHereNode = null; + } + if (toolStripAdornerWindowService != null) + { + toolStripAdornerWindowService.Invalidate(bounds); + } + } + + /// + /// This private function is called to ROLLBACK the current Insitu editing mode. + /// + private void RollBack() + { + if (commitedEditorNode != null) + { + int index = MenuItem.DropDownItems.IndexOf(commitedEditorNode); + Debug.Assert(index != -1, "Invalid Index"); + ToolStripDropDownItem editedItem = (ToolStripDropDownItem)MenuItem.DropDownItems[index + 1]; + if (editedItem != null) + { + editedItem.Visible = true; + } + MenuItem.DropDown.Items.Remove(commitedEditorNode); + if (commitedTemplateNode != null) + { + commitedTemplateNode.RollBack(); + commitedTemplateNode.CloseEditor(); + commitedTemplateNode = null; + } + if (commitedEditorNode != null) + { + commitedEditorNode.Dispose(); + commitedEditorNode = null; + } + } + } + + /// + /// Remove Body glyphs when the dropDown is closed. + /// + private void RemoveBodyGlyphs(ToolStripDropDownItem item) + { + if (item != null) + { + foreach (ToolStripItem ddItem in item.DropDownItems) + { + ToolStripItemDesigner dropDownItemDesigner = (ToolStripItemDesigner)designerHost.GetDesigner(ddItem); + if (dropDownItemDesigner != null) + { + ControlBodyGlyph glyph = dropDownItemDesigner.bodyGlyph; + if (glyph != null && toolStripAdornerWindowService != null && toolStripAdornerWindowService.DropDownAdorner.Glyphs.Contains(glyph)) + { + toolStripAdornerWindowService.DropDownAdorner.Glyphs.Remove(glyph); + dropDownItemDesigner.bodyGlyph = null; + } + } + } + } + } + + /// + /// Remove glyphs per item + /// + internal void RemoveItemBodyGlyph(ToolStripItem item) + { + if (item != null) + { + ToolStripItemDesigner itemDesigner = (ToolStripItemDesigner)designerHost.GetDesigner(item); + if (itemDesigner != null) + { + ControlBodyGlyph glyph = itemDesigner.bodyGlyph; + if (glyph != null && toolStripAdornerWindowService != null && toolStripAdornerWindowService.DropDownAdorner.Glyphs.Contains(glyph)) + { + toolStripAdornerWindowService.DropDownAdorner.Glyphs.Remove(glyph); + itemDesigner.bodyGlyph = null; + } + } + } + } + + /// + /// Helper function to remove and then re-add the glyphs. + /// + internal void ResetGlyphs(ToolStripDropDownItem item) + { + //Reset the glyphs only if the DropDown is visible. + if (item.DropDown.Visible) + { + InitializeBodyGlyphsForItems(false, item); + InitializeBodyGlyphsForItems(true, item); + } + } + + /// + /// Set the Selection after a insitu edit is complete. + /// + internal override bool SetSelection(bool enterKeyPressed) + { + if (enterKeyPressed) + { + if (!initialized) + { + InitializeDropDown(); + } + //set the selection to our new item + if (selSvc != null) + { + if (KeyboardHandlingService != null) + { + int count = 0; + if (MenuItem.DropDownDirection != ToolStripDropDownDirection.AboveLeft && MenuItem.DropDownDirection != ToolStripDropDownDirection.AboveRight) + { + // index to the last item. + count = MenuItem.DropDownItems.Count; + count--; + } + selSvc.SetSelectedComponents(new object[] { MenuItem }, SelectionTypes.Replace); + if (count >= 0) + { + KeyboardHandlingService.SelectedDesignerControl = MenuItem.DropDownItems[count]; + selSvc.SetSelectedComponents(null, SelectionTypes.Replace); + } + } + } + return true; + } + return false; + } + + /// + /// Returns true if the visible property should be persisted in code gen. + /// + private bool ShouldSerializeDoubleClickEnabled() => (bool)ShadowProperties["DoubleClickEnabled"]; + + /// + /// Returns true if the CheckOnClick property should be persisted in code gen. + /// + private bool ShouldSerializeCheckOnClick() => (bool)ShadowProperties["CheckOnClick"]; + + /// + /// Returns true if the CheckOnClick property should be persisted in code gen. + /// + private bool ShouldSerializeDropDown() => (customDropDown != null); + + /// + /// Returns true if the visible property should be persisted in code gen. + /// + private bool ShouldSerializeVisible() => !Visible; + + /// + /// This Function is called thru the ToolStripEditorManager which is listening for the F2 command. + /// + internal override void ShowEditNode(bool clicked) + { + if (MenuItem == null) + { + return; + } + try + { + if (MenuItem.Owner is ToolStripDropDown) + { + parentItem = ((ToolStripDropDown)(MenuItem.Owner)).OwnerItem; + //need to inform the owner tha we want to enter insitu mode + if (designerHost != null) + { + IDesigner designer = designerHost.GetDesigner(parentItem); + if (designer is ToolStripMenuItemDesigner) + { + ((ToolStripMenuItemDesigner)designer).EnterInSituEdit(MenuItem); + } + } + } + // We come here for TOP LEVEL MENUITEM .. So call base version... + else + { + base.ShowEditNode(clicked); + } + } + catch (CheckoutException checkoutException) + { + // Do not crash on canceled checkout + if (checkoutException.Equals(CheckoutException.Canceled)) + { + return; + } + else + { + throw; + } + } + } + + /// + /// This Function would select all items starting form oldSelection to the Current MenuItem. + /// + private void SelectItems(ToolStripDropDownItem oldSelection, ISelectionService selSvc) + { + ToolStripDropDown ownerDropDown = (ToolStripDropDown)MenuItem.Owner; + int maxIndex = Math.Max(ownerDropDown.Items.IndexOf(oldSelection), ownerDropDown.Items.IndexOf(MenuItem)); + int minIndex = Math.Min(ownerDropDown.Items.IndexOf(oldSelection), ownerDropDown.Items.IndexOf(MenuItem)); + ToolStripItem[] selectedItems = new ToolStripItem[maxIndex - minIndex + 1]; + int i = 0; + while (minIndex <= maxIndex) + { + selectedItems[i] = ownerDropDown.Items[minIndex]; + i++; + minIndex++; + } + selSvc.SetSelectedComponents(selectedItems); + + } + + /// + /// Shows ALL the owner DropDowns if passed in MenuItem is Selected + /// + internal void ShowOwnerDropDown(ToolStripDropDownItem currentSelection) + { + // We MIGHT HAVE TO START TOP - DOWN Instead of BOTTOM-UP. Sometimes we DONT get the Owner POPUP and hence all the popup are parented to Wrong guy. + while (currentSelection != null && currentSelection.Owner is ToolStripDropDown) + { + ToolStripDropDown currentDropDown = (ToolStripDropDown)(currentSelection.Owner); + currentSelection = (ToolStripDropDownItem)currentDropDown.OwnerItem; + if (currentSelection != null && !currentSelection.DropDown.Visible) + { + ToolStripMenuItemDesigner currentSelectionDesigner = designerHost.GetDesigner(currentSelection) as ToolStripMenuItemDesigner; + if (currentSelectionDesigner != null) + { + currentSelectionDesigner.InitializeDropDown(); + } + } + else if (currentDropDown is ContextMenuStrip && !currentDropDown.Visible) + { + // ContextMenuStrip does not have an owner item, and need be shown with different method + ToolStripDropDownDesigner dropDownDesigner = (ToolStripDropDownDesigner)designerHost.GetDesigner(currentDropDown); + if (dropDownDesigner != null) + { + dropDownDesigner.ShowMenu(null); + } + } + } + } + + /// + /// This will listen to the necessary dropDown events... now we add the events on selection and unhook when the dropDown is closed. + /// + internal void UnHookEvents() + { + if (MenuItem != null) + { + MenuItem.DropDown.Closing -= new ToolStripDropDownClosingEventHandler(OnDropDownClosing); + MenuItem.DropDownOpening -= new EventHandler(DropDownItem_DropDownOpening); + MenuItem.DropDownOpened -= new EventHandler(DropDownItem_DropDownOpened); + MenuItem.DropDownClosed -= new EventHandler(DropDownItem_DropDownClosed); + MenuItem.DropDown.Resize -= new EventHandler(DropDownResize); + MenuItem.DropDown.ItemAdded -= new ToolStripItemEventHandler(OnItemAdded); + MenuItem.DropDown.Paint -= new PaintEventHandler(DropDownPaint); + MenuItem.DropDown.LocationChanged -= new EventHandler(DropDownLocationChanged); + MenuItem.DropDown.Click -= new EventHandler(DropDownClick); + } + } + + /// + /// The glyph we put over the items. Basically this sets the hit-testable area of the item itself. + /// + internal class ToolStripDropDownGlyph : Glyph + { + private Rectangle _bounds; + internal ToolStripDropDownGlyph(Rectangle bounds, System.Windows.Forms.Design.Behavior.Behavior b) : base(b) + { + _bounds = bounds; + } + + /// + /// Abstract method that forces Glyph implementations to provide hit test logic. Given any point - if the Glyph has decided to be involved with that location, the Glyph will need to return a valid Cursor. Otherwise, returning null will cause the the BehaviorService to simply ignore it. + /// + public override Cursor GetHitTest(Point p) + { + if (_bounds.Contains(p)) + { + return Cursors.Default; + } + return null; + } + + /// + /// Overrides Glyph::Paint - this implementation does nothing. + /// + public override void Paint(PaintEventArgs pe) + { + } + } + + /// + /// The transparent behavior on top of the DropDownGlyphs. + /// + internal class DropDownBehavior : ControlDesigner.TransparentBehavior + { + /// + /// Constructor that accepts the related ControlDesigner. + /// + private ToolStripMenuItemDesigner menuItemDesigner; + + internal DropDownBehavior(ControlDesigner designer, ToolStripMenuItemDesigner menuItemDesigner) : base(designer) + { + this.menuItemDesigner = menuItemDesigner; + } + + /// + /// Drag drop support on the DropDown... + /// + public override void OnDragEnter(Glyph g, DragEventArgs e) + { + ToolStripItemDataObject data = e.Data as ToolStripItemDataObject; + if (data != null) + { + e.Effect = (Control.ModifierKeys == Keys.Control) ? DragDropEffects.Copy : DragDropEffects.Move; + } + else + { + base.OnDragEnter(g, e); + } + } + + /// + /// Drag drop support on the DropDown... + /// + public override void OnDragOver(Glyph g, DragEventArgs e) + { + ToolStripItemDataObject data = e.Data as ToolStripItemDataObject; + if (data != null) + { + e.Effect = (Control.ModifierKeys == Keys.Control) ? DragDropEffects.Copy : DragDropEffects.Move; + } + else + { + base.OnDragOver(g, e); + } + } + + /// + /// Drag drop support on the DropDown... + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public override void OnDragDrop(Glyph g, DragEventArgs e) + { + ToolStripItemDataObject data = e.Data as ToolStripItemDataObject; + if (data != null) + { + ToolStripItem primaryItem = data.PrimarySelection; + IDesignerHost host = (IDesignerHost)primaryItem.Site.GetService(typeof(IDesignerHost)); + ToolStripDropDown parentToolStrip = primaryItem.GetCurrentParent() as ToolStripDropDown; + ToolStripDropDownItem ownerItem = null; + if (parentToolStrip != null) + { + ownerItem = parentToolStrip.OwnerItem as ToolStripDropDownItem; + } + Debug.Assert(ownerItem != null, "How can ownerItem be null for a menu item on a dropdown?"); + if (ownerItem != null && host != null) + { + string transDesc; + ArrayList components = data.DragComponents; + int primaryIndex = -1; + bool copy = (e.Effect == DragDropEffects.Copy); + if (components.Count == 1) + { + string name = TypeDescriptor.GetComponentName(components[0]); + if (name == null || name.Length == 0) + { + name = components[0].GetType().Name; + } + transDesc = string.Format(copy ? SR.BehaviorServiceCopyControl : SR.BehaviorServiceMoveControl, name); + } + else + { + transDesc = string.Format(copy ? SR.BehaviorServiceCopyControls : SR.BehaviorServiceMoveControls, components.Count); + } + // create a transaction so this happens as an atomic unit. + DesignerTransaction changeParent = host.CreateTransaction(transDesc); + try + { + IComponentChangeService changeSvc = (IComponentChangeService)primaryItem.Site.GetService(typeof(IComponentChangeService)); + if (changeSvc != null) + changeSvc.OnComponentChanging(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"]); + // If we are copying, then we want to make a copy of the components we are dragging + if (copy) + { + // Remember the primary selection if we had one + if (primaryItem != null) + { + primaryIndex = components.IndexOf(primaryItem); + } + ToolStripKeyboardHandlingService keyboardHandlingService = (ToolStripKeyboardHandlingService)primaryItem.Site.GetService(typeof(ToolStripKeyboardHandlingService)); + if (keyboardHandlingService != null) + { + keyboardHandlingService.CopyInProgress = true; + } + components = DesignerUtils.CopyDragObjects(components, primaryItem.Site) as ArrayList; + + if (keyboardHandlingService != null) + { + keyboardHandlingService.CopyInProgress = false; + } + if (primaryIndex != -1) + { + primaryItem = components[primaryIndex] as ToolStripItem; + } + } + + if (e.Effect == DragDropEffects.Move || copy) + { + // Add the item. + foreach (ToolStripItem toolItem in components) + { + parentToolStrip.Items.Add(toolItem); + } + + // Open the DropDown for the PrimarySelection before the DRAG-DROP operation. + if (primaryItem is ToolStripDropDownItem dropDownItem) + { + if (host.GetDesigner(dropDownItem) is ToolStripMenuItemDesigner dropDownItemDesigner) + { + dropDownItemDesigner.InitializeDropDown(); + } + } + //Set the Selection .. + menuItemDesigner.selSvc.SetSelectedComponents(new IComponent[] { primaryItem }, SelectionTypes.Primary | SelectionTypes.Replace); + } + if (changeSvc != null) + { + changeSvc.OnComponentChanged(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"], null, null); + } + + //fire extra changing/changed events so that the order is "restored" after undo/redo + if (copy) + { + if (changeSvc != null) + { + changeSvc.OnComponentChanging(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"]); + changeSvc.OnComponentChanged(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"], null, null); + } + } + + //If Parent is DropDown... we have to manage the Glyphs .... + if (ownerItem != null) + { + if (host.GetDesigner(ownerItem) is ToolStripMenuItemDesigner ownerDesigner) + { + ownerDesigner.InitializeBodyGlyphsForItems(false, ownerItem); + ownerDesigner.InitializeBodyGlyphsForItems(true, ownerItem); + } + } + // Refresh the BehaviorService. + BehaviorService bSvc = (BehaviorService)primaryItem.Site.GetService(typeof(BehaviorService)); + if (bSvc != null) + { + bSvc.SyncSelection(); + } + } + catch + { + if (changeParent != null) + { + changeParent.Cancel(); + changeParent = null; + } + } + finally + { + if (changeParent != null) + changeParent.Commit(); + changeParent = null; + } + } + } + } + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs new file mode 100644 index 00000000000..c3843f0f75d --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs @@ -0,0 +1,2074 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms.Design.Behavior; +using static System.Windows.Forms.UnsafeNativeMethods; + +namespace System.Windows.Forms.Design +{ + /// + /// This internal class wraps the InSitu Editor. The editor is a runtime Winbar control which contains a leftButton (for image), centerLabel (for text) which gets swaped by a centerTextBox (when InSitu is ON). The ToolStripTemplateNode is also responsible for intercepting the Escape and Enter keys and implements the IMenuStatusHandler so that it can commit and rollback as required. Finally this ToolStripTemplateNode has a private class ItemTypeToolStripMenuItem for adding ToolStripItem types to the Dropdown for addItemButton. + /// + internal class ToolStripTemplateNode : IMenuStatusHandler + { + private const int GLYPHBORDER = 1; + private const int GLYPHINSET = 2; + + private const int TOOLSTRIP_TEMPLATE_HEIGHT_ORIGINAL = 22; + private const int TEMPLATE_HEIGHT_ORIGINAL = 19; + private const int TOOLSTRIP_TEMPLATE_WIDTH_ORIGINAL = 120; + private const int TEMPLATE_WIDTH_ORIGINAL = 31; + private const int MINITOOLSTRIP_DROPDOWN_BUTTON_WIDTH_ORIGINAL = 11; + private const int TEMPLATE_HOTREGION_WIDTH_ORIGINAL = 9; + private const int MINITOOLSTRIP_TEXTBOX_WIDTH_ORIGINAL = 90; + + private static int TOOLSTRIP_TEMPLATE_HEIGHT = TOOLSTRIP_TEMPLATE_HEIGHT_ORIGINAL; + private static int TEMPLATE_HEIGHT = TEMPLATE_HEIGHT_ORIGINAL; + private static int TOOLSTRIP_TEMPLATE_WIDTH = TOOLSTRIP_TEMPLATE_WIDTH_ORIGINAL; + private static int TEMPLATE_WIDTH = TEMPLATE_WIDTH_ORIGINAL; + private static int MINITOOLSTRIP_DROPDOWN_BUTTON_WIDTH = MINITOOLSTRIP_DROPDOWN_BUTTON_WIDTH_ORIGINAL; + private static int TEMPLATE_HOTREGION_WIDTH = TEMPLATE_HOTREGION_WIDTH_ORIGINAL; + private static int MINITOOLSTRIP_TEXTBOX_WIDTH = MINITOOLSTRIP_TEXTBOX_WIDTH_ORIGINAL; + + private static bool s_isScalingInitialized = false; + internal const string CenterLabelName = "centerLabel"; + + // Component for this InSitu Editor... (this is a ToolStripItem) that wants to go into InSitu + private readonly IComponent _component; + // Current Designer for the comopenent that in InSitu mode + private IDesigner _designer = null; + //Get DesignerHost. + private readonly IDesignerHost _designerHost = null; + // Menu Commands to override + private readonly MenuCommand[] _commands; + // MenuCommands to Add + private readonly MenuCommand[] _addCommands; + // Actual InSitu Editor and its components... + private TransparentToolStrip _miniToolStrip; + // Center Label for MenuStrip TemplateNode + private ToolStripLabel _centerLabel; + // SplitButton reAdded for ToolStrip specific TemplateNode + private ToolStripSplitButton _addItemButton; + //swaped in text... + private ToolStripControlHost _centerTextBox; + + //reqd as rtb does accept Enter.. + internal bool ignoreFirstKeyUp = false; + + // This is the Bounding Rectangle for the ToolStripTemplateNode. This is set by the itemDesigner in terms of the "AdornerWindow" bounds. The ToolStripEditorManager uses this Bounds to actually activate the editor on the AdornerWindow. + private Rectangle _boundingRect; + // Keeps track of Insitu Mode. + private bool _inSituMode = false; + // Tells whether the editorNode is listening to Menu commands. + private bool _active = false; + + // Need to keep a track of Last Selection to uncheck it. This is the Checked property on ToolStripItems on the Menu. We check this cached in value to the current Selection on the addItemButton and if different then uncheck the Checked for this lastSelection.. Check for the currentSelection and finally save the currentSelection as the lastSelection for future check. + private ItemTypeToolStripMenuItem _lastSelection = null; + + // This is the renderer used to Draw the Strips..... + private MiniToolStripRenderer _renderer; + // This is the Type that the user has selected for the new Item + private Type _itemType; + //Get the ToolStripKeyBoardService to notify that the TemplateNode is Active and so it shouldnt process the KeyMessages. + private ToolStripKeyboardHandlingService _toolStripKeyBoardService; + + //Cached ISelectionService + private ISelectionService _selectionService; + //Cached BehaviorService + private BehaviorService _behaviorService; + //ControlHost for selection on mouseclicks + private DesignerToolStripControlHost _controlHost = null; + // On DropDowns the component passed in is the parent (ownerItem) and hence we need the reference for actual item + private ToolStripItem _activeItem = null; + + private EventHandler _onActivated; + private EventHandler _onClosed; + private EventHandler _onDeactivated; + private MenuCommand _oldUndoCommand = null; + private MenuCommand _oldRedoCommand = null; + // The DropDown for the TemplateNode + private NewItemsContextMenuStrip _contextMenu; + // the Hot Region within the templateNode ... this is used for the menustrips + private Rectangle _hotRegion; + + private bool _imeModeSet = false; + //DesignSurface to hook up to the Flushed event + private DesignSurface _designSurface = null; + // Is system context menu displayed for the insitu text box? + private bool _isSystemContextMenuDisplayed = false; + // delay population of custom menu items until ready to open the drop down + private bool _isPopulated; + + public ToolStripTemplateNode(IComponent component, string text, Image image) + { + _component = component; + // In most of the cases this is true; except for ToolStripItems on DropDowns. the toolstripMenuItemDesigners sets the public property in those cases. + _activeItem = component as ToolStripItem; + _designerHost = (IDesignerHost)component.Site.GetService(typeof(IDesignerHost)); + _designer = _designerHost.GetDesigner(component); + _designSurface = (DesignSurface)component.Site.GetService(typeof(DesignSurface)); + if (_designSurface != null) + { + _designSurface.Flushed += new EventHandler(OnLoaderFlushed); + } + + if (!s_isScalingInitialized) + { + if (DpiHelper.IsScalingRequired) + { + // dimensions of the "Type Here" text box + TOOLSTRIP_TEMPLATE_HEIGHT = DpiHelper.LogicalToDeviceUnitsY(TOOLSTRIP_TEMPLATE_HEIGHT_ORIGINAL); + TEMPLATE_HEIGHT = DpiHelper.LogicalToDeviceUnitsY(TEMPLATE_HEIGHT_ORIGINAL); + TOOLSTRIP_TEMPLATE_WIDTH = DpiHelper.LogicalToDeviceUnitsX(TOOLSTRIP_TEMPLATE_WIDTH_ORIGINAL); + TEMPLATE_WIDTH = DpiHelper.LogicalToDeviceUnitsX(TEMPLATE_WIDTH_ORIGINAL); + //hotregion is the arrow button next to "Type Here" box + TEMPLATE_HOTREGION_WIDTH = DpiHelper.LogicalToDeviceUnitsX(TEMPLATE_HOTREGION_WIDTH_ORIGINAL); + + MINITOOLSTRIP_DROPDOWN_BUTTON_WIDTH = DpiHelper.LogicalToDeviceUnitsX(MINITOOLSTRIP_DROPDOWN_BUTTON_WIDTH_ORIGINAL); + MINITOOLSTRIP_TEXTBOX_WIDTH = DpiHelper.LogicalToDeviceUnitsX(MINITOOLSTRIP_TEXTBOX_WIDTH_ORIGINAL); + } + s_isScalingInitialized = true; + } + + SetupNewEditNode(this, text, image, component); + _commands = new MenuCommand[] { + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyMoveUp), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyMoveDown), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyMoveLeft), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyMoveRight), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.Delete), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.Cut), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.Copy), + + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeUp), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeDown), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeLeft), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeRight), + + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeySizeWidthIncrease), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeySizeHeightIncrease), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeySizeWidthDecrease), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeySizeHeightDecrease), + + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeWidthIncrease), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeHeightIncrease), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeWidthDecrease), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeHeightDecrease) + + }; + + _addCommands = new MenuCommand[] { + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.Undo), + new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.Redo) + }; + } + + /// + /// This property enables / disables Menu Command Handler. + /// + public bool Active + { + get => _active; + set + { + if (_active != value) + { + _active = value; + + if (KeyboardService != null) + { + KeyboardService.TemplateNodeActive = value; + } + + if (_active) + { + //Active.. Fire Activated + OnActivated(new EventArgs()); + if (KeyboardService != null) + { + KeyboardService.ActiveTemplateNode = this; + } + + IMenuCommandService menuService = (IMenuCommandService)_component.Site.GetService(typeof(IMenuCommandService)); + if (menuService != null) + { + _oldUndoCommand = menuService.FindCommand(MenuCommands.Undo); + if (_oldUndoCommand != null) + { + menuService.RemoveCommand(_oldUndoCommand); + } + + _oldRedoCommand = menuService.FindCommand(MenuCommands.Redo); + if (_oldRedoCommand != null) + { + menuService.RemoveCommand(_oldRedoCommand); + } + + // Disable the Commands + for (int i = 0; i < _addCommands.Length; i++) + { + _addCommands[i].Enabled = false; + menuService.AddCommand(_addCommands[i]); + } + } + + // Listen to command and key events + IEventHandlerService ehs = (IEventHandlerService)_component.Site.GetService(typeof(IEventHandlerService)); + if (ehs != null) + { + ehs.PushHandler(this); + } + } + else + { + //Active == false.. Fire Deactivated + OnDeactivated(new EventArgs()); + if (KeyboardService != null) + { + KeyboardService.ActiveTemplateNode = null; + } + + IMenuCommandService menuService = (IMenuCommandService)_component.Site.GetService(typeof(IMenuCommandService)); + if (menuService != null) + { + for (int i = 0; i < _addCommands.Length; i++) + { + menuService.RemoveCommand(_addCommands[i]); + } + } + + if (_oldUndoCommand != null) + { + menuService.AddCommand(_oldUndoCommand); + } + + if (_oldRedoCommand != null) + { + menuService.AddCommand(_oldRedoCommand); + } + + // Stop listening to command and key events + IEventHandlerService ehs = (IEventHandlerService)_component.Site.GetService(typeof(IEventHandlerService)); + if (ehs != null) + { + ehs.PopHandler(this); + } + } + } + } + } + + // Need to have a reference of the actual item that is edited. + public ToolStripItem ActiveItem + { + get => _activeItem; + set => _activeItem = value; + } + + public event EventHandler Activated + { + add => _onActivated += value; + remove => _onActivated -= value; + } + + /// + /// Returns the Bounds of this ToolStripTemplateNode. + /// + public Rectangle Bounds + { + get => _boundingRect; + set => _boundingRect = value; + } + + public DesignerToolStripControlHost ControlHost + { + get => _controlHost; + set => _controlHost = value; + } + + /// + /// This is the designer contextMenu that pops when rightclicked on the TemplateNode. + /// + private ContextMenuStrip DesignerContextMenu + { + get + { + BaseContextMenuStrip templateNodeContextMenu = new BaseContextMenuStrip(_component.Site, _controlHost) + { + Populated = false + }; + templateNodeContextMenu.GroupOrdering.Clear(); + templateNodeContextMenu.GroupOrdering.AddRange(new string[] { StandardGroups.Code, StandardGroups.Custom, StandardGroups.Selection, StandardGroups.Edit }); + templateNodeContextMenu.Text = "CustomContextMenu"; + + TemplateNodeCustomMenuItemCollection templateNodeCustomMenuItemCollection = new TemplateNodeCustomMenuItemCollection(_component.Site, _controlHost); + foreach (ToolStripItem item in templateNodeCustomMenuItemCollection) + { + templateNodeContextMenu.Groups[StandardGroups.Custom].Items.Add(item); + } + return templateNodeContextMenu; + } + } + + public event EventHandler Deactivated + { + add => _onDeactivated += value; + remove => _onDeactivated -= value; + } + + public event EventHandler Closed + { + add => _onClosed += value; + remove => _onClosed -= value; + } + + /// + /// This property returns the actual editor ToolStrip. + /// + public ToolStrip EditorToolStrip + { + get => _miniToolStrip; + } + + /// + /// This property returns the actual editor ToolStrip. + /// + internal TextBox EditBox + { + get => (_centerTextBox != null) ? (TextBox)_centerTextBox.Control : null; + } + + /// + /// HotRegion within the templateNode. this is the region which responds to the mouse. + /// + public Rectangle HotRegion + { + get => _hotRegion; + set => _hotRegion = value; + } + + /// + /// value to suggest if IME mode is set. + /// + public bool IMEModeSet + { + get => _imeModeSet; + set => _imeModeSet = value; + } + + /// + /// KeyBoardHandling service. + /// + private ToolStripKeyboardHandlingService KeyboardService + { + get + { + if (_toolStripKeyBoardService == null) + { + _toolStripKeyBoardService = (ToolStripKeyboardHandlingService)_component.Site.GetService(typeof(ToolStripKeyboardHandlingService)); + } + return _toolStripKeyBoardService; + } + } + + /// + /// SelectionService. + /// + private ISelectionService SelectionService + { + get + { + if (_selectionService == null) + { + _selectionService = (ISelectionService)_component.Site.GetService(typeof(ISelectionService)); + } + return _selectionService; + } + } + + + private BehaviorService BehaviorService + { + get + { + if (_behaviorService == null) + { + _behaviorService = (BehaviorService)_component.Site.GetService(typeof(BehaviorService)); + } + return _behaviorService; + } + } + + /// + /// Type of the new Item to be added. + /// + public Type ToolStripItemType + { + get => _itemType; + set => _itemType = value; + } + + /// + /// Is system context menu for the insitu edit box displayed?. + /// + internal bool IsSystemContextMenuDisplayed + { + get => _isSystemContextMenuDisplayed; + set => _isSystemContextMenuDisplayed = value; + } + + /// + /// Helper function to add new Item when the DropDownItem (in the ToolStripTemplateNode) is clicked + /// + private void AddNewItemClick(object sender, EventArgs e) + { + // Close the DropDown.. Important for Morphing .... + if (_addItemButton != null) + { + _addItemButton.DropDown.Visible = false; + } + + if (_component is ToolStrip && SelectionService != null) + { + // Stop the Designer from closing the Overflow if its open + ToolStripDesigner designer = _designerHost.GetDesigner(_component) as ToolStripDesigner; + try + { + if (designer != null) + { + designer.DontCloseOverflow = true; + } + SelectionService.SetSelectedComponents(new object[] { _component }); + } + finally + { + if (designer != null) + { + designer.DontCloseOverflow = false; + } + } + } + + ItemTypeToolStripMenuItem senderItem = (ItemTypeToolStripMenuItem)sender; + if (_lastSelection != null) + { + _lastSelection.Checked = false; + } + + // set the appropriate Checked state + senderItem.Checked = true; + _lastSelection = senderItem; + // Set the property used in the CommitEditor (.. ) to add the correct Type. + ToolStripItemType = senderItem.ItemType; + //Select the parent before adding + ToolStrip parent = _controlHost.GetCurrentParent() as ToolStrip; + // this will add the item to the ToolStrip.. + if (parent is MenuStrip) + { + CommitEditor(true, true, false); + } + else + { + // In case of toolStrips/StatusStrip we want the currently added item to be selected instead of selecting the next item + CommitEditor(true, false, false); + } + + if (KeyboardService != null) + { + KeyboardService.TemplateNodeActive = false; + } + } + + /// + /// Called when the user clicks the CenterLabel of the ToolStripTemplateNode. + /// + private void CenterLabelClick(object sender, MouseEventArgs e) + { + //For Right Button we show the DesignerContextMenu... + if (e.Button == MouseButtons.Right) + { + //Dont show the DesignerContextMenu if there is any active templateNode. + if (KeyboardService != null && KeyboardService.TemplateNodeActive) + { + return; + } + if (KeyboardService != null) + { + KeyboardService.SelectedDesignerControl = _controlHost; + } + SelectionService.SetSelectedComponents(null, SelectionTypes.Replace); + if (BehaviorService != null) + { + Point loc = BehaviorService.ControlToAdornerWindow(_miniToolStrip); + loc = BehaviorService.AdornerWindowPointToScreen(loc); + loc.Offset(e.Location); + DesignerContextMenu.Show(loc); + } + } + else + { + if (_hotRegion.Contains(e.Location) && !KeyboardService.TemplateNodeActive) + { + if (KeyboardService != null) + { + KeyboardService.SelectedDesignerControl = _controlHost; + } + SelectionService.SetSelectedComponents(null, SelectionTypes.Replace); + ToolStripDropDown oldContextMenu = _contextMenu; + // PERF: Consider refresh mechanism for the derived items. + if (oldContextMenu != null) + { + oldContextMenu.Closed -= new ToolStripDropDownClosedEventHandler(OnContextMenuClosed); + oldContextMenu.Closing -= OnContextMenuClosing; + oldContextMenu.Opened -= new EventHandler(OnContextMenuOpened); + oldContextMenu.Dispose(); + } + _contextMenu = null; + ShowDropDownMenu(); + + } + else + { + // Remember the click position. + ToolStripDesigner.s_lastCursorPosition = Cursor.Position; + + if (_designer is ToolStripDesigner) + { + if (KeyboardService.TemplateNodeActive) + { + KeyboardService.ActiveTemplateNode.Commit(false, false); + } + // cause a selectionChange... + if (SelectionService.PrimarySelection == null) + { + SelectionService.SetSelectedComponents(new object[] { _component }, SelectionTypes.Replace); + } + + KeyboardService.SelectedDesignerControl = _controlHost; + SelectionService.SetSelectedComponents(null, SelectionTypes.Replace); + ((ToolStripDesigner)_designer).ShowEditNode(true); + } + if (_designer is ToolStripMenuItemDesigner) + { + // cache the serviceProvider (Site) since the component can potential get disposed after the call to CommitAndSelect(); + IServiceProvider svcProvider = _component.Site as IServiceProvider; + // Commit any InsituEdit Node. + if (KeyboardService.TemplateNodeActive) + { + if (_component is ToolStripItem currentItem) + { + // We have clicked the TemplateNode of a visible Item .. so just commit the current Insitu... + if (currentItem.Visible) + { + // If templateNode Active .. commit + KeyboardService.ActiveTemplateNode.Commit(false, false); + } + else //we have clicked the templateNode of a Invisible Item ... so a dummyItem. In this case select the item. + { + // If templateNode Active .. commit and Select + KeyboardService.ActiveTemplateNode.Commit(false, true); + } + } + else //If Component is not a ToolStripItem + { + KeyboardService.ActiveTemplateNode.Commit(false, false); + } + } + if (_designer != null) + { + ((ToolStripMenuItemDesigner)_designer).EditTemplateNode(true); + } + else + { + ISelectionService cachedSelSvc = (ISelectionService)svcProvider.GetService(typeof(ISelectionService)); + if (cachedSelSvc.PrimarySelection is ToolStripItem selectedItem && _designerHost != null) + { + if (_designerHost.GetDesigner(selectedItem) is ToolStripMenuItemDesigner itemDesigner) + { + //Invalidate the item only if its toplevel. + if (!selectedItem.IsOnDropDown) + { + Rectangle bounds = itemDesigner.GetGlyphBounds(); + ToolStripDesignerUtils.GetAdjustedBounds(selectedItem, ref bounds); + if (svcProvider.GetService(typeof(BehaviorService)) is BehaviorService bSvc) + { + bSvc.Invalidate(bounds); + } + } + itemDesigner.EditTemplateNode(true); + } + } + } + } + } + } + } + + /// + /// Painting of the templateNode on MouseEnter. + /// + private void CenterLabelMouseEnter(object sender, EventArgs e) + { + if (_renderer != null && !KeyboardService.TemplateNodeActive) + { + if (_renderer.State != (int)TemplateNodeSelectionState.HotRegionSelected) + { + _renderer.State = (int)TemplateNodeSelectionState.MouseOverLabel; + _miniToolStrip.Invalidate(); + } + } + } + + /// + /// Painting of the templateNode on MouseMove + /// + private void CenterLabelMouseMove(object sender, MouseEventArgs e) + { + if (_renderer != null && !KeyboardService.TemplateNodeActive) + { + if (_renderer.State != (int)TemplateNodeSelectionState.HotRegionSelected) + { + if (_hotRegion.Contains(e.Location)) + { + _renderer.State = (int)TemplateNodeSelectionState.MouseOverHotRegion; + } + else + { + _renderer.State = (int)TemplateNodeSelectionState.MouseOverLabel; + } + _miniToolStrip.Invalidate(); + } + } + } + + /// + /// Painting of the templateNode on MouseLeave + /// + private void CenterLabelMouseLeave(object sender, EventArgs e) + { + if (_renderer != null && !KeyboardService.TemplateNodeActive) + { + if (_renderer.State != (int)TemplateNodeSelectionState.HotRegionSelected) + { + _renderer.State = (int)TemplateNodeSelectionState.None; + } + if (KeyboardService != null && KeyboardService.SelectedDesignerControl == _controlHost) + { + _renderer.State = (int)TemplateNodeSelectionState.TemplateNodeSelected; + } + _miniToolStrip.Invalidate(); + } + } + + /// + /// Painting of the templateNode on MouseEnter + /// + private void CenterTextBoxMouseEnter(object sender, EventArgs e) + { + if (_renderer != null) + { + _renderer.State = (int)TemplateNodeSelectionState.TemplateNodeSelected; + _miniToolStrip.Invalidate(); + } + } + + /// + /// Painting of the templateNode on TextBox mouseLeave (in case of MenuStrip) + /// + private void CenterTextBoxMouseLeave(object sender, EventArgs e) + { + if (_renderer != null && !Active) + { + _renderer.State = (int)TemplateNodeSelectionState.None; + _miniToolStrip.Invalidate(); + } + } + + /// + /// This Internal function is called from the ToolStripItemDesigner to relinquish the resources used by the EditorToolStrip. This Fucntion disposes the ToolStrip and its components and also clears the event handlers associated. + /// + internal void CloseEditor() + { + if (_miniToolStrip != null) + { + Active = false; + if (_lastSelection != null) + { + _lastSelection.Dispose(); + _lastSelection = null; + } + + if (_component is ToolStrip strip) + { + strip.RightToLeftChanged -= new System.EventHandler(OnRightToLeftChanged); + } + else + { + if (_component is ToolStripDropDownItem stripItem) + { + stripItem.RightToLeftChanged -= new System.EventHandler(OnRightToLeftChanged); + } + } + + if (_centerLabel != null) + { + _centerLabel.MouseUp -= new MouseEventHandler(CenterLabelClick); + _centerLabel.MouseEnter -= new EventHandler(CenterLabelMouseEnter); + _centerLabel.MouseMove -= new MouseEventHandler(CenterLabelMouseMove); + _centerLabel.MouseLeave -= new EventHandler(CenterLabelMouseLeave); + _centerLabel.Dispose(); + _centerLabel = null; + } + + if (_addItemButton != null) + { + _addItemButton.MouseMove -= new MouseEventHandler(OnMouseMove); + _addItemButton.MouseUp -= new MouseEventHandler(OnMouseUp); + _addItemButton.MouseDown -= new MouseEventHandler(OnMouseDown); + _addItemButton.DropDownOpened -= new EventHandler(OnAddItemButtonDropDownOpened); + _addItemButton.DropDown.Dispose(); + _addItemButton.Dispose(); + _addItemButton = null; + } + if (_contextMenu != null) + { + _contextMenu.Closed -= new ToolStripDropDownClosedEventHandler(OnContextMenuClosed); + _contextMenu.Closing -= OnContextMenuClosing; + _contextMenu.Opened -= new EventHandler(OnContextMenuOpened); + _contextMenu = null; + } + + _miniToolStrip.MouseLeave -= new EventHandler(OnMouseLeave); + _miniToolStrip.Dispose(); + _miniToolStrip = null; + // Surface can be null. VS Whidbey #572862 + if (_designSurface != null) + { + _designSurface.Flushed -= new EventHandler(OnLoaderFlushed); + _designSurface = null; + } + _designer = null; + OnClosed(new EventArgs()); + } + } + + /// + /// This internal Function is called by item designers to ROLLBACK the current Insitu editing mode. + /// + internal void Commit(bool enterKeyPressed, bool tabKeyPressed) + { + // Commit only if we are still available !! + if (_miniToolStrip != null && _inSituMode) + { + string text = ((TextBox)(_centerTextBox.Control)).Text; + if (string.IsNullOrEmpty(text)) + { + RollBack(); + } + else + { + CommitEditor(true, enterKeyPressed, tabKeyPressed); + } + } + } + + /// + /// Internal function that would commit the TemplateNode + /// + internal void CommitAndSelect() + { + Commit(false, false); + } + + private void CommitTextToDesigner(string text, bool commit, bool enterKeyPressed, bool tabKeyPressed) + { + if (commit && (_designer is ToolStripDesigner || _designer is ToolStripMenuItemDesigner)) + { + Type selectedType; + // If user has typed in "-" then Add a Separator only on DropDowns. + if (text == "-" && _designer is ToolStripMenuItemDesigner) + { + ToolStripItemType = typeof(ToolStripSeparator); + } + if (ToolStripItemType != null) + { + selectedType = ToolStripItemType; + ToolStripItemType = null; + } + else + { + Type[] supportedTypes = ToolStripDesignerUtils.GetStandardItemTypes(_component); + selectedType = supportedTypes[0]; + } + if (_designer is ToolStripDesigner) + { + ((ToolStripDesigner)_designer).AddNewItem(selectedType, text, enterKeyPressed, tabKeyPressed); + } + else + { + ((ToolStripItemDesigner)_designer).CommitEdit(selectedType, text, commit, enterKeyPressed, tabKeyPressed); + } + } + else if (_designer is ToolStripItemDesigner) + { + ((ToolStripItemDesigner)_designer).CommitEdit(_designer.Component.GetType(), text, commit, enterKeyPressed, tabKeyPressed); + } + } + + /// + /// This private function performs the job of commiting the current InSitu Editor. This will call the CommitEdit(...) function for the appropriate designers so that they can actually do their own Specific things for commiting (or ROLLBACKING) the Insitu Edit mode. The commit flag is used for commit or rollback. BE SURE TO ALWAYS call ExitInSituEdit from this function to put the EditorToolStrip in a sane "NON EDIT" mode. + /// + private void CommitEditor(bool commit, bool enterKeyPressed, bool tabKeyPressed) + { + // After the node is commited the templateNode gets the selection. But the original selection is not invalidated. consider following case + // FOO -> BAR -> TEMPLATENODE node + // When the TemplateNode is committed "FOO" is selected but after the commit is complete, The TemplateNode gets the selection but "FOO" is never invalidated and hence retains selection. So we get the selection and then invalidate it at the end of this function. Get the currentSelection to invalidate + string text = (_centerTextBox != null) ? ((TextBox)(_centerTextBox.Control)).Text : string.Empty; + ExitInSituEdit(); + FocusForm(); + CommitTextToDesigner(text, commit, enterKeyPressed, tabKeyPressed); + // finally Invalidate the selection rect ... + if (SelectionService.PrimarySelection is ToolStripItem curSel) + { + if (_designerHost != null) + { + if (_designerHost.GetDesigner(curSel) is ToolStripItemDesigner designer) + { + Rectangle invalidateBounds = designer.GetGlyphBounds(); + ToolStripDesignerUtils.GetAdjustedBounds(curSel, ref invalidateBounds); + invalidateBounds.Inflate(GLYPHBORDER, GLYPHBORDER); + Region rgn = new Region(invalidateBounds); + invalidateBounds.Inflate(-GLYPHINSET, -GLYPHINSET); + rgn.Exclude(invalidateBounds); + if (BehaviorService != null) + { + BehaviorService.Invalidate(rgn); + } + rgn.Dispose(); + } + } + } + } + + /// + /// The ToolStripTemplateNode enters into InSitu Edit Mode through this Function. This Function is called by FocusEditor( ) which starts the InSitu. The centerLabel is SWAPPED by centerTextBox and the ToolStripTemplateNode is Ready for Text. Settting "Active = true" pushes the IEventHandler which now intercepts the Escape and Enter keys to ROLLBACK or COMMIT the InSitu Editing respectively. + /// + private void EnterInSituEdit() + { + if (!_inSituMode) + { + // Listen For Commandss.... + if (_miniToolStrip.Parent != null) + { + _miniToolStrip.Parent.SuspendLayout(); + } + try + { + Active = true; + _inSituMode = true; + // set the renderer state to Selected... + if (_renderer != null) + { + _renderer.State = (int)TemplateNodeSelectionState.TemplateNodeSelected; + } + + // Set UP textBox for InSitu + TextBox tb = new TemplateTextBox(_miniToolStrip, this) + { + BorderStyle = BorderStyle.FixedSingle, + Text = _centerLabel.Text, + ForeColor = SystemColors.WindowText + }; + _centerTextBox = new ToolStripControlHost(tb) + { + Dock = DockStyle.None, + AutoSize = false, + Width = MINITOOLSTRIP_TEXTBOX_WIDTH + }; + + if (_activeItem is ToolStripDropDownItem item && !item.IsOnDropDown) + { + _centerTextBox.Margin = new System.Windows.Forms.Padding(1, 2, 1, 3); + } + else + { + _centerTextBox.Margin = new System.Windows.Forms.Padding(1); + } + _centerTextBox.Size = _miniToolStrip.DisplayRectangle.Size - _centerTextBox.Margin.Size; + _centerTextBox.Name = "centerTextBox"; + _centerTextBox.MouseEnter += new EventHandler(CenterTextBoxMouseEnter); + _centerTextBox.MouseLeave += new EventHandler(CenterTextBoxMouseLeave); + int index = _miniToolStrip.Items.IndexOf(_centerLabel); + //swap in our insitu textbox + if (index != -1) + { + _miniToolStrip.Items.Insert(index, _centerTextBox); + _miniToolStrip.Items.Remove(_centerLabel); + } + + tb.KeyUp += new KeyEventHandler(OnKeyUp); + tb.KeyDown += new KeyEventHandler(OnKeyDown); + tb.SelectAll(); + Control baseComponent = null; + if (_designerHost != null) + { + baseComponent = (Control)_designerHost.RootComponent; + NativeMethods.SendMessage(baseComponent.Handle, NativeMethods.WM_SETREDRAW, 0, 0); + tb.Focus(); + NativeMethods.SendMessage(baseComponent.Handle, NativeMethods.WM_SETREDRAW, 1, 0); + } + } + finally + { + if (_miniToolStrip.Parent != null) + { + _miniToolStrip.Parent.ResumeLayout(); + } + } + } + } + + /// + /// The ToolStripTemplateNode exits from InSitu Edit Mode through this Function. This Function is called by CommitEditor( ) which stops the InSitu. The centerTextBox is SWAPPED by centerLabel and the ToolStripTemplateNode is exits the InSitu Mode. Settting "Active = false" pops the IEventHandler. + /// + private void ExitInSituEdit() + { + // put the ToolStripTemplateNode back into "non edit state" + if (_centerTextBox != null && _inSituMode) + { + if (_miniToolStrip.Parent != null) + { + _miniToolStrip.Parent.SuspendLayout(); + } + try + { + //if going insitu with a real item, set & select all the text + int index = _miniToolStrip.Items.IndexOf(_centerTextBox); + //validate index + if (index != -1) + { + _centerLabel.Text = SR.ToolStripDesignerTemplateNodeEnterText; + //swap in our insitu textbox + _miniToolStrip.Items.Insert(index, _centerLabel); + _miniToolStrip.Items.Remove(_centerTextBox); + ((TextBox)(_centerTextBox.Control)).KeyUp -= new KeyEventHandler(OnKeyUp); + ((TextBox)(_centerTextBox.Control)).KeyDown -= new KeyEventHandler(OnKeyDown); + } + + _centerTextBox.MouseEnter -= new EventHandler(CenterTextBoxMouseEnter); + _centerTextBox.MouseLeave -= new EventHandler(CenterTextBoxMouseLeave); + _centerTextBox.Dispose(); + _centerTextBox = null; + _inSituMode = false; + //reset the Size.... + SetWidth(null); + } + finally + { + if (_miniToolStrip.Parent != null) + { + _miniToolStrip.Parent.ResumeLayout(); + } + // POP of the Handler !!! + Active = false; + } + } + } + + /// + /// This internal function is called from ToolStripItemDesigner to put the current item into InSitu Edit Mode. + /// + internal void FocusEditor(ToolStripItem currentItem) + { + if (currentItem != null) + { + _centerLabel.Text = currentItem.Text; + } + EnterInSituEdit(); + } + + /// + /// Called when the user enters into the InSitu edit mode.This keeps the fdesigner Form Active..... + /// + private void FocusForm() + { + if (_component.Site.GetService(typeof(ISplitWindowService)) is DesignerFrame designerFrame) + { + if (_designerHost != null) + { + Control baseComponent = (Control)_designerHost.RootComponent; + NativeMethods.SendMessage(baseComponent.Handle, NativeMethods.WM_SETREDRAW, 0, 0); + designerFrame.Focus(); + NativeMethods.SendMessage(baseComponent.Handle, NativeMethods.WM_SETREDRAW, 1, 0); + } + } + } + + protected void OnActivated(EventArgs e) + { + _onActivated?.Invoke(this, e); + } + + private void OnAddItemButtonDropDownOpened(object sender, EventArgs e) + { + _addItemButton.DropDown.Focus(); + } + + protected void OnClosed(EventArgs e) + { + _onClosed?.Invoke(this, e); + } + + /// + /// Painting of the templateNode on when the contextMenu is closed + /// + private void OnContextMenuClosed(object sender, ToolStripDropDownClosedEventArgs e) + { + if (_renderer != null) + { + _renderer.State = (int)TemplateNodeSelectionState.TemplateNodeSelected; + _miniToolStrip.Invalidate(); + } + } + + private void OnContextMenuClosing(object sender, ToolStripDropDownClosingEventArgs e) + { + if (_addItemButton == null) + { + _miniToolStrip.RaiseStateChangeEvent(); + } + } + + /// + /// Set the KeyBoardService member, so the designer knows that the "ContextMenu" is opened. + /// + private void OnContextMenuOpened(object sender, EventArgs e) + { + // Disable All Commands .. the Commands would be reenabled by AddNewItemClick call. + if (KeyboardService != null) + { + KeyboardService.TemplateNodeContextMenuOpen = true; + } + } + + protected void OnDeactivated(EventArgs e) + { + _onDeactivated?.Invoke(this, e); + } + + /// + /// Called by the design surface when it is being flushed. This will save any changes made to TemplateNode. + /// + private void OnLoaderFlushed(object sender, EventArgs e) + { + Commit(false, false); + } + + /// + /// This is small HACK. For some reason if the InSituEditor's textbox has focus the escape key is lost and the menu service doesnt get it.... but the textbox gets it. So need to check for the escape key here and call CommitEditor(false) which will ROLLBACK the edit. + /// + private void OnKeyUp(object sender, KeyEventArgs e) + { + if (IMEModeSet) + { + return; + } + switch (e.KeyCode) + { + + case Keys.Up: + Commit(false, true); + if (KeyboardService != null) + { + KeyboardService.ProcessUpDown(false); + } + break; + case Keys.Down: + Commit(true, false); + break; + case Keys.Escape: + CommitEditor(false, false, false); + break; + case Keys.Return: + if (ignoreFirstKeyUp) + { + ignoreFirstKeyUp = false; + return; + } + OnKeyDefaultAction(sender, e); + break; + } + } + + /// + /// Select text on KeyDown. + /// + private void OnKeyDown(object sender, KeyEventArgs e) + { + if (IMEModeSet) + { + return; + } + if (e.KeyCode == Keys.A && (e.KeyData & Keys.Control) != 0) + { + if (sender is TextBox t) + { + t.SelectAll(); + } + } + } + + /// + /// Check for the Enter key here and call CommitEditor(true) which will COMMIT the edit. + /// + private void OnKeyDefaultAction(object sender, EventArgs e) + { + //exit Insitu with commiting.... + Active = false; + Debug.Assert(_centerTextBox.Control != null, "The TextBox is null"); + if (_centerTextBox.Control != null) + { + string text = ((TextBox)(_centerTextBox.Control)).Text; + if (string.IsNullOrEmpty(text)) + { + CommitEditor(false, false, false); + } + else + { + CommitEditor(true, true, false); + } + } + } + + /// + /// Called when the delete menu item is selected. + /// + private void OnMenuCut(object sender, EventArgs e) + { + } + + /// + /// Show ContextMenu if the Right Mouse button was pressed and we have received the following MouseUp + /// + private void OnMouseUp(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Right) + { + if (BehaviorService != null) + { + Point loc = BehaviorService.ControlToAdornerWindow(_miniToolStrip); + loc = BehaviorService.AdornerWindowPointToScreen(loc); + loc.Offset(e.Location); + DesignerContextMenu.Show(loc); + } + } + } + + /// + /// Set the selection to the component. + /// + private void OnMouseDown(object sender, MouseEventArgs e) + { + if (KeyboardService != null) + { + KeyboardService.SelectedDesignerControl = _controlHost; + } + SelectionService.SetSelectedComponents(null, SelectionTypes.Replace); + } + + /// + /// Painting on the button for mouse Move. + /// + private void OnMouseMove(object sender, MouseEventArgs e) + { + _renderer.State = (int)TemplateNodeSelectionState.None; + if (_renderer != null) + { + if (_addItemButton != null) + { + if (_addItemButton.ButtonBounds.Contains(e.Location)) + { + _renderer.State = (int)TemplateNodeSelectionState.SplitButtonSelected; + } + else if (_addItemButton.DropDownButtonBounds.Contains(e.Location)) + { + _renderer.State = (int)TemplateNodeSelectionState.DropDownSelected; + } + } + _miniToolStrip.Invalidate(); + } + } + + /// + /// Painting on the button for mouse Leave. + /// + private void OnMouseLeave(object sender, EventArgs e) + { + if (SelectionService != null) + { + if (SelectionService.PrimarySelection is ToolStripItem && _renderer != null && _renderer.State != (int)TemplateNodeSelectionState.HotRegionSelected) + { + _renderer.State = (int)TemplateNodeSelectionState.None; + } + if (KeyboardService != null && KeyboardService.SelectedDesignerControl == _controlHost) + { + _renderer.State = (int)TemplateNodeSelectionState.TemplateNodeSelected; + } + _miniToolStrip.Invalidate(); + } + } + + private void OnRightToLeftChanged(object sender, EventArgs e) + { + if (sender is ToolStrip strip) + { + _miniToolStrip.RightToLeft = strip.RightToLeft; + } + else + { + ToolStripDropDownItem stripItem = sender as ToolStripDropDownItem; + _miniToolStrip.RightToLeft = stripItem.RightToLeft; + } + } + + /// + /// Intercept invokation of specific commands and keys + /// + public bool OverrideInvoke(MenuCommand cmd) + { + for (int i = 0; i < _commands.Length; i++) + { + if (_commands[i].CommandID.Equals(cmd.CommandID)) + { + if (cmd.CommandID == MenuCommands.Delete || cmd.CommandID == MenuCommands.Cut || cmd.CommandID == MenuCommands.Copy) + { + _commands[i].Invoke(); + return true; + } + } + } + return false; + } + + /// + /// Intercept invokation of specific commands and keys + /// + public bool OverrideStatus(MenuCommand cmd) + { + + for (int i = 0; i < _commands.Length; i++) + { + if (_commands[i].CommandID.Equals(cmd.CommandID)) + { + cmd.Enabled = false; + return true; + } + } + + return false; + } + + /// + /// This internal Function is called by item designers to ROLLBACK the current Insitu editing mode. + /// + internal void RollBack() + { + // RollBack only iff we are still available !! + if (_miniToolStrip != null && _inSituMode) + { + CommitEditor(false, false, false); + } + } + + internal void ShowContextMenu(Point pt) + { + DesignerContextMenu.Show(pt); + } + + internal void ShowDropDownMenu() + { + if (_addItemButton != null) + { + if (!_isPopulated) + { + _isPopulated = true; + ToolStripDesignerUtils.GetCustomNewItemDropDown(_contextMenu, _component, null, new EventHandler(AddNewItemClick), false, _component.Site); + } + _addItemButton.ShowDropDown(); + } + else + { + if (BehaviorService != null) + { + Point loc = BehaviorService.ControlToAdornerWindow(_miniToolStrip); + loc = BehaviorService.AdornerWindowPointToScreen(loc); + Rectangle translatedBounds = new Rectangle(loc, _miniToolStrip.Size); + _miniToolStrip.RaiseStateChangeEvent(); + + if (_contextMenu == null) + { + _isPopulated = true; + _contextMenu = ToolStripDesignerUtils.GetNewItemDropDown(_component, null, new EventHandler(AddNewItemClick), false, _component.Site, true); + _contextMenu.Closed += new ToolStripDropDownClosedEventHandler(OnContextMenuClosed); + _contextMenu.Closing += OnContextMenuClosing; + _contextMenu.Opened += new EventHandler(OnContextMenuOpened); + _contextMenu.Text = "ItemSelectionMenu"; + } + else if (!_isPopulated) + { + _isPopulated = true; + ToolStripDesignerUtils.GetCustomNewItemDropDown(_contextMenu, _component, null, new EventHandler(AddNewItemClick), false, _component.Site); + } + + if (_component is ToolStrip strip) + { + _contextMenu.RightToLeft = strip.RightToLeft; + } + else + { + if (_component is ToolStripDropDownItem stripItem) + { + _contextMenu.RightToLeft = stripItem.RightToLeft; + } + } + _contextMenu.Show(translatedBounds.X, translatedBounds.Y + translatedBounds.Height); + _contextMenu.Focus(); + if (_renderer != null) + { + _renderer.State = (int)TemplateNodeSelectionState.HotRegionSelected; + _miniToolStrip.Invalidate(); + } + } + } + } + + /// + /// This function sets up the MenuStrip specific TemplateNode. + /// + private void SetUpMenuTemplateNode(ToolStripTemplateNode owner, string text, Image image, IComponent currentItem) + { + _centerLabel = new ToolStripLabel + { + Text = text, + AutoSize = false, + IsLink = false, + AccessibleDescription = SR.ToolStripDesignerTemplateNodeLabelToolTip, + AccessibleRole = AccessibleRole.Text, + + Margin = new Padding(1) + }; + if (currentItem is ToolStripDropDownItem) + { + _centerLabel.Margin = new Padding(1, 2, 1, 3); + } + _centerLabel.Padding = new Padding(0, 1, 0, 0); + _centerLabel.Name = CenterLabelName; + _centerLabel.Size = _miniToolStrip.DisplayRectangle.Size - _centerLabel.Margin.Size; + _centerLabel.ToolTipText = SR.ToolStripDesignerTemplateNodeLabelToolTip; + _centerLabel.MouseUp += new MouseEventHandler(CenterLabelClick); + _centerLabel.MouseEnter += new EventHandler(CenterLabelMouseEnter); + _centerLabel.MouseMove += new MouseEventHandler(CenterLabelMouseMove); + _centerLabel.MouseLeave += new EventHandler(CenterLabelMouseLeave); + + _miniToolStrip.Items.AddRange(new ToolStripItem[] { _centerLabel }); + } + + /// + /// This function sets up TemplateNode for ToolStrip, StatusStrip, ContextMenuStrip. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + private void SetUpToolTemplateNode(ToolStripTemplateNode owner, string text, Image image, IComponent component) + { + _addItemButton = new ToolStripSplitButton + { + AutoSize = false, + Margin = new Padding(1) + }; + _addItemButton.Size = _miniToolStrip.DisplayRectangle.Size - _addItemButton.Margin.Size; + _addItemButton.DropDownButtonWidth = MINITOOLSTRIP_DROPDOWN_BUTTON_WIDTH; + _addItemButton.DisplayStyle = ToolStripItemDisplayStyle.Image; + if (component is StatusStrip) + { + _addItemButton.ToolTipText = SR.ToolStripDesignerTemplateNodeSplitButtonStatusStripToolTip; + } + else + { + _addItemButton.ToolTipText = SR.ToolStripDesignerTemplateNodeSplitButtonToolTip; + } + + _addItemButton.MouseDown += new System.Windows.Forms.MouseEventHandler(OnMouseDown); + _addItemButton.MouseMove += new System.Windows.Forms.MouseEventHandler(OnMouseMove); + _addItemButton.MouseUp += new System.Windows.Forms.MouseEventHandler(OnMouseUp); + _addItemButton.DropDownOpened += OnAddItemButtonDropDownOpened; + _contextMenu = ToolStripDesignerUtils.GetNewItemDropDown(component, null, new EventHandler(AddNewItemClick), false, component.Site, false); + _contextMenu.Text = "ItemSelectionMenu"; + _contextMenu.Closed += new ToolStripDropDownClosedEventHandler(OnContextMenuClosed); + _contextMenu.Closing += OnContextMenuClosing; + _contextMenu.Opened += new EventHandler(OnContextMenuOpened); + _addItemButton.DropDown = _contextMenu; + _addItemButton.AccessibleName = SR.ToolStripDesignerTemplateNodeSplitButtonStatusStripAccessibleName; + _addItemButton.AccessibleRole = AccessibleRole.ButtonDropDown; + // Set up default item and image. + try + { + if (_addItemButton.DropDownItems.Count > 0) + { + ItemTypeToolStripMenuItem firstItem = (ItemTypeToolStripMenuItem)_addItemButton.DropDownItems[0]; + _addItemButton.ImageTransparentColor = Color.Lime; + Bitmap bmp = new Bitmap(typeof(ToolStripTemplateNode), "ToolStripTemplateNode.bmp"); + if (DpiHelper.IsScalingRequired) + { + bmp.MakeTransparent(Color.Lime); + DpiHelper.ScaleBitmapLogicalToDevice(ref bmp); + } + _addItemButton.Image = bmp; + _addItemButton.DefaultItem = firstItem; + } + Debug.Assert(_addItemButton.DropDownItems.Count > 0); + } + catch (Exception ex) + { + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + _miniToolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + _addItemButton + }); + } + + /// + /// This method does actual edit node creation. + /// + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + private void SetupNewEditNode(ToolStripTemplateNode owner, string text, Image image, IComponent currentItem) + { + // setup the MINIToolStrip host... + _renderer = new MiniToolStripRenderer(owner); + _miniToolStrip = new TransparentToolStrip(owner); + if (currentItem is ToolStrip strip) + { + _miniToolStrip.RightToLeft = strip.RightToLeft; + strip.RightToLeftChanged += new System.EventHandler(OnRightToLeftChanged); + // Make TransparentToolStrip has the same "Site" as ToolStrip. This could make sure TransparentToolStrip has the same design time behavior as ToolStrip. + _miniToolStrip.Site = strip.Site; + } + if (currentItem is ToolStripDropDownItem stripItem) + { + _miniToolStrip.RightToLeft = stripItem.RightToLeft; + stripItem.RightToLeftChanged += new System.EventHandler(this.OnRightToLeftChanged); + } + _miniToolStrip.SuspendLayout(); + _miniToolStrip.CanOverflow = false; + _miniToolStrip.Cursor = System.Windows.Forms.Cursors.Default; + _miniToolStrip.Dock = System.Windows.Forms.DockStyle.None; + _miniToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + _miniToolStrip.Name = "miniToolStrip"; + _miniToolStrip.TabIndex = 0; + _miniToolStrip.Visible = true; + _miniToolStrip.Renderer = _renderer; + + // Add items to the Template ToolStrip depending upon the Parent Type... + if (currentItem is MenuStrip || currentItem is ToolStripDropDownItem) + { + SetUpMenuTemplateNode(owner, text, image, currentItem); + _miniToolStrip.AccessibleRole = AccessibleRole.ComboBox; + _miniToolStrip.Text = text; + } + else + { + SetUpToolTemplateNode(owner, text, image, currentItem); + _miniToolStrip.AccessibleRole = AccessibleRole.ButtonDropDown; + } + _miniToolStrip.MouseLeave += new System.EventHandler(OnMouseLeave); + _miniToolStrip.ResumeLayout(); + } + + /// + /// This method does sets the width of the Editor (_miniToolStrip) based on the text passed in. + /// + internal void SetWidth(string text) + { + // REVIEW: is this function necessary anymore? + if (string.IsNullOrEmpty(text)) + { + _miniToolStrip.Width = _centerLabel.Width + 2; + } + else + { + _centerLabel.Text = text; + } + } + + /// + /// Private class that implements the textBox for the InSitu Editor. + /// + private class TemplateTextBox : TextBox + { + TransparentToolStrip parent; + ToolStripTemplateNode owner; + private const int IMEMODE = 229; + + public TemplateTextBox(TransparentToolStrip parent, ToolStripTemplateNode owner) : base() + { + this.parent = parent; + this.owner = owner; + AutoSize = false; + Multiline = false; + } + + /// + /// Get Parent Handle. + /// + private bool IsParentWindow(IntPtr hWnd) + { + if (hWnd == parent.Handle) + { + return true; + } + return false; + } + + protected override bool IsInputKey(Keys keyData) + { + switch (keyData & Keys.KeyCode) + { + case Keys.Return: + owner.Commit(true, false); + return true; + } + return base.IsInputKey(keyData); + } + + /// + /// Process the IMEMode message.. + /// + protected override bool ProcessDialogKey(Keys keyData) + { + if ((int)keyData == IMEMODE) + { + owner.IMEModeSet = true; + } + else + { + owner.IMEModeSet = false; + owner.ignoreFirstKeyUp = false; + } + return base.ProcessDialogKey(keyData); + } + + /// + /// Process the WNDPROC for WM_KILLFOCUS to commit the Insitu Editor.. + /// + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_KILLFOCUS: + base.WndProc(ref m); + IntPtr focussedWindow = (IntPtr)m.WParam; + if (!IsParentWindow(focussedWindow)) + { + owner.Commit(false, false); + } + break; + + // 1.Slowly click on a menu strip item twice to make it editable, while the item's dropdown menu is visible + // 2.Select the text of the item and right click on it + // 3.Left click 'Copy' or 'Cut' in the context menu IDE crashed because left click in step3 invoked glyph behavior, which commited and destroyed the insitu edit box and thus the 'copy' or 'cut' action has no text to work with. Thus need to block glyph behaviors while the context menu is displayed. + case NativeMethods.WM_CONTEXTMENU: + owner.IsSystemContextMenuDisplayed = true; + base.WndProc(ref m); + owner.IsSystemContextMenuDisplayed = false; + break; + default: + base.WndProc(ref m); + break; + } + } + } + + /// + /// Private class to Change the Winbar to a TransparentWinbar. Our EditorToolStrip is a TranparentToolStrip so that it picks up the itemColor. + /// + public class TransparentToolStrip : ToolStrip + { + ToolStripTemplateNode owner; + IComponent currentItem; + + public TransparentToolStrip(ToolStripTemplateNode owner) + { + this.owner = owner; + currentItem = owner._component; + TabStop = true; + SetStyle(ControlStyles.Selectable, true); + AutoSize = false; + AccessibleName = SR.ToolStripDesignerToolStripAccessibleName; + AccessibleRole = AccessibleRole.ComboBox; + } + + /// + /// Owner TemplateNode.. + /// + public ToolStripTemplateNode TemplateNode + { + get => owner; + } + + /// + /// Commit the node and move to next selection. + /// + private void CommitAndSelectNext(bool forward) + { + owner.Commit(false, true); + if (owner.KeyboardService != null) + { + owner.KeyboardService.ProcessKeySelect(!forward, null); + } + } + + /// + /// get current selection. + /// + private ToolStripItem GetSelectedItem() + { + ToolStripItem selectedItem = null; + for (int i = 0; i < Items.Count; i++) + { + if (Items[i].Selected) + { + selectedItem = Items[i]; + } + } + return selectedItem; + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public override Size GetPreferredSize(Size proposedSize) + { + if (currentItem is ToolStripDropDownItem) + { + return new Size(Width, TOOLSTRIP_TEMPLATE_HEIGHT); + } + else + { + return new Size(Width, TEMPLATE_HEIGHT); + } + } + + /// + /// Process the Tab Key.. + /// + private bool ProcessTabKey(bool forward) + { + // Give the ToolStripItem first dibs + ToolStripItem item = this.GetSelectedItem(); + if (item is ToolStripControlHost) + { + + CommitAndSelectNext(forward); + return true; + } + return false; + } + + /// + /// Process the Dialog Keys for the Templatenode ToolStrip.. + /// + protected override bool ProcessDialogKey(Keys keyData) + { + bool retVal = false; + if (owner.Active) + { + if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) + { + Keys keyCode = (Keys)keyData & Keys.KeyCode; + switch (keyCode) + { + case Keys.Tab: + retVal = ProcessTabKey((keyData & Keys.Shift) == Keys.None); + break; + } + } + + if (retVal) + { + return retVal; + } + } + return base.ProcessDialogKey(keyData); + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) + { + if (currentItem is ToolStripDropDownItem) + { + base.SetBoundsCore(x, y, TOOLSTRIP_TEMPLATE_WIDTH, TOOLSTRIP_TEMPLATE_HEIGHT, specified); + } + else if (currentItem is MenuStrip) + { + base.SetBoundsCore(x, y, TOOLSTRIP_TEMPLATE_WIDTH, TEMPLATE_HEIGHT, specified); + } + else + { + base.SetBoundsCore(x, y, TEMPLATE_WIDTH, TEMPLATE_HEIGHT, specified); + } + } + + internal void RaiseStateChangeEvent() + { + AccessibilityNotifyClients(AccessibleEvents.StateChange, -1); + } + + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_GETOBJECT: + if (owner._addItemButton == null) + { + // only adding patterns to _miniToolStrip associated with MenuStrip or ContextMenu + // m.Result = AutomationInteropProvider.ReturnRawElementProvider(Handle, m.WParam, m.LParam, (IRawElementProviderSimple)(new TransparentToolStripUiaProvider(this))); + return; + } + break; + } + base.WndProc(ref m); + } + } + + /* + internal class TransparentToolStripUiaProvider : ControlUiaProvider, IExpandCollapseProvider + { + public TransparentToolStripUiaProvider(TransparentToolStrip owner) : base(owner) + { + } + + private ToolStripTemplateNode TemplateNode + { + get => (_owner as TransparentToolStrip).TemplateNode; + } + + #region IValueProvider + public override void SetValue(string newValue) + { + TemplateNode.CommitTextToDesigner(newValue, true, true, false); + } + #endregion + + #region Expand/Collapse pattern + public override object GetPatternProvider(int patternId) + { + if (patternId == ExpandCollapsePatternIdentifiers.Pattern.Id && TemplateNode != null) + { + return this as IExpandCollapseProvider; + } + + return base.GetPatternProvider(patternId); + } + + public ExpandCollapseState ExpandCollapseState + { + get + { + NewItemsContextMenuStrip menu = TemplateNode._contextMenu; + return ((menu != null) && menu.Visible) ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed; + } + } + + public void Expand() + { + TemplateNode.ShowDropDownMenu(); + } + + public void Collapse() + { + // Drop down for the MenuStrip is implemented as a context menu and thus closes as soon as keyboard focus leaves it. When Collapse method is invoked in Inspect, context menu is already closed. + TemplateNode._contextMenu?.Hide(); + } + #endregion + } + */ + + /// + /// Private class that implements the custom Renderer for the TemplateNode ToolStrip. + /// + public class MiniToolStripRenderer : ToolStripSystemRenderer + { + private int state = (int)TemplateNodeSelectionState.None; + private Color selectedBorderColor; + private Color defaultBorderColor; + private Color dropDownMouseOverColor; + private Color dropDownMouseDownColor; + private Color toolStripBorderColor; + private ToolStripTemplateNode owner; + private Rectangle hotRegion = Rectangle.Empty; + + public MiniToolStripRenderer(ToolStripTemplateNode owner) : base() + { + //Add Colors + this.owner = owner; + selectedBorderColor = Color.FromArgb(46, 106, 197); + defaultBorderColor = Color.FromArgb(171, 171, 171); + dropDownMouseOverColor = Color.FromArgb(193, 210, 238); + dropDownMouseDownColor = Color.FromArgb(152, 181, 226); + toolStripBorderColor = Color.White; + } + + /// + /// Current state of the TemplateNode UI.. + /// + public int State + { + get => state; + set => state = value; + } + + /// + /// Custom method to draw DOWN arrow on the DropDown. + /// + private void DrawArrow(Graphics g, Rectangle bounds) + { + bounds.Width--; + DrawArrow(new ToolStripArrowRenderEventArgs(g, null, bounds, SystemInformation.HighContrast ? Color.Black : SystemColors.ControlText, ArrowDirection.Down)); + } + + /// + /// Drawing different DropDown states. + /// + private void DrawDropDown(Graphics g, Rectangle bounds, int state) + { + switch (state) + { + case 1: //TemplateNodeSelected + case 4: //MouseOver + using (LinearGradientBrush brush = new LinearGradientBrush(bounds, Color.White, defaultBorderColor, LinearGradientMode.Vertical)) + { + g.FillRectangle(brush, bounds); + } + break; + case 5: //MouseOnthe HotRegion + using (SolidBrush b = new SolidBrush(dropDownMouseOverColor)) + { + g.FillRectangle(b, hotRegion); + } + break; + case 6: //HotRegionSelected + using (SolidBrush b = new SolidBrush(dropDownMouseDownColor)) + { + g.FillRectangle(b, hotRegion); + } + break; + } + DrawArrow(g, bounds); + } + + protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) + { + if (owner._component is MenuStrip || owner._component is ToolStripDropDownItem) + { + Graphics g = e.Graphics; + g.Clear(toolStripBorderColor); + } + else + { + base.OnRenderToolStripBackground(e); + } + } + + + /// + /// Render ToolStrip Border + /// + protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) + { + Graphics g = e.Graphics; + Rectangle bounds = new Rectangle(Point.Empty, e.ToolStrip.Size); + Pen selectborderPen = new Pen(toolStripBorderColor); + Rectangle drawRect = new Rectangle(bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + g.DrawRectangle(selectborderPen, drawRect); + selectborderPen.Dispose(); + } + + /// + /// Render the Center Label on the TemplateNode ToolStrip. + /// + protected override void OnRenderLabelBackground(ToolStripItemRenderEventArgs e) + { + base.OnRenderLabelBackground(e); + ToolStripItem item = e.Item; + Graphics g = e.Graphics; + Rectangle bounds = new Rectangle(Point.Empty, item.Size); + Rectangle drawRect = new Rectangle(bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + Pen borderPen = new Pen(defaultBorderColor); + if (state == (int)TemplateNodeSelectionState.TemplateNodeSelected) //state Template node is selected. + { + using (SolidBrush brush = new SolidBrush(toolStripBorderColor)) + { + g.FillRectangle(brush, drawRect); + } + if (owner.EditorToolStrip.RightToLeft == RightToLeft.Yes) + { + hotRegion = new Rectangle(bounds.Left + 2, bounds.Top + 2, TEMPLATE_HOTREGION_WIDTH, bounds.Bottom - 4); + } + else + { + hotRegion = new Rectangle(bounds.Right - TEMPLATE_HOTREGION_WIDTH - 2, bounds.Top + 2, TEMPLATE_HOTREGION_WIDTH, bounds.Bottom - 4); + } + owner.HotRegion = hotRegion; + + // do the Actual Drawing + DrawDropDown(g, hotRegion, state); + + borderPen.Color = Color.Black; + item.ForeColor = defaultBorderColor; + g.DrawRectangle(borderPen, drawRect); + } + + if (state == (int)TemplateNodeSelectionState.MouseOverLabel) //state Template node is selected. + { + if (owner.EditorToolStrip.RightToLeft == RightToLeft.Yes) + { + hotRegion = new Rectangle(bounds.Left + 2, bounds.Top + 2, TEMPLATE_HOTREGION_WIDTH, bounds.Bottom - 4); + } + else + { + hotRegion = new Rectangle(bounds.Right - TEMPLATE_HOTREGION_WIDTH - 2, bounds.Top + 2, TEMPLATE_HOTREGION_WIDTH, bounds.Bottom - 4); + } + owner.HotRegion = hotRegion; + + g.Clear(toolStripBorderColor); + DrawDropDown(g, hotRegion, state); + borderPen.Color = Color.Black; + borderPen.DashStyle = DashStyle.Dot; + g.DrawRectangle(borderPen, drawRect); + } + + if (state == (int)TemplateNodeSelectionState.MouseOverHotRegion) + { + g.Clear(toolStripBorderColor); + DrawDropDown(g, hotRegion, state); + borderPen.Color = Color.Black; + borderPen.DashStyle = DashStyle.Dot; + item.ForeColor = defaultBorderColor; + g.DrawRectangle(borderPen, drawRect); + } + + if (state == (int)TemplateNodeSelectionState.HotRegionSelected) + { + g.Clear(toolStripBorderColor); + DrawDropDown(g, hotRegion, state); + borderPen.Color = Color.Black; + item.ForeColor = defaultBorderColor; + g.DrawRectangle(borderPen, drawRect); + } + + if (state == (int)TemplateNodeSelectionState.None) //state Template node is not selected. + { + g.Clear(toolStripBorderColor); + g.DrawRectangle(borderPen, drawRect); + item.ForeColor = defaultBorderColor; + } + borderPen.Dispose(); + } + + /// + /// Render the splitButton on the TemplateNode ToolStrip.. + /// + protected override void OnRenderSplitButtonBackground(ToolStripItemRenderEventArgs e) + { + // DONT CALL THE BASE AS IT DOESNT ALLOW US TO RENDER THE DROPDOWN BUTTON .... + //base.OnRenderSplitButtonBackground(e); + Graphics g = e.Graphics; + if (e.Item is ToolStripSplitButton splitButton) + { + // Get the DropDownButton Bounds + Rectangle buttonBounds = splitButton.DropDownButtonBounds; + // Draw the White Divider Line... + using (Pen p = new Pen(toolStripBorderColor)) + { + g.DrawLine(p, buttonBounds.Left, buttonBounds.Top + 1, buttonBounds.Left, buttonBounds.Bottom - 1); + } + + Rectangle bounds = new Rectangle(Point.Empty, splitButton.Size); + Pen selectborderPen = null; + bool splitButtonSelected = false; + if (splitButton.DropDownButtonPressed) + { + //Button is pressed + state = 0; + Rectangle fillRect = new Rectangle(buttonBounds.Left + 1, buttonBounds.Top, buttonBounds.Right, buttonBounds.Bottom); + using (SolidBrush brush = new SolidBrush(dropDownMouseDownColor)) + { + g.FillRectangle(brush, fillRect); + } + splitButtonSelected = true; + } + else if (state == (int)TemplateNodeSelectionState.SplitButtonSelected) + { + using (SolidBrush brush = new SolidBrush(dropDownMouseOverColor)) + { + g.FillRectangle(brush, splitButton.ButtonBounds); + } + splitButtonSelected = true; + } + else if (state == (int)TemplateNodeSelectionState.DropDownSelected) + { + Rectangle fillRect = new Rectangle(buttonBounds.Left + 1, buttonBounds.Top, buttonBounds.Right, buttonBounds.Bottom); + using (SolidBrush brush = new SolidBrush(dropDownMouseOverColor)) + { + g.FillRectangle(brush, fillRect); + } + splitButtonSelected = true; + } + else if (state == (int)TemplateNodeSelectionState.TemplateNodeSelected) + { + splitButtonSelected = true; + } + + if (splitButtonSelected) + { + //DrawSeleted Boder + selectborderPen = new Pen(selectedBorderColor); + } + else + { + // Draw Gray Border + selectborderPen = new Pen(defaultBorderColor); + } + + Rectangle drawRect = new Rectangle(bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + g.DrawRectangle(selectborderPen, drawRect); + selectborderPen.Dispose(); + + // Draw the Arrow + DrawArrow(new ToolStripArrowRenderEventArgs(g, splitButton, splitButton.DropDownButtonBounds, SystemColors.ControlText, ArrowDirection.Down)); + } + } + + protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) + { + ToolStripItem item = e.Item as ToolStripLabel; + if (item != null && String.Equals(item.Name, CenterLabelName, StringComparison.InvariantCulture) && SystemInformation.HighContrast) + { + // "Type Here" node always has white background, text should be painted in black + e.TextColor = Color.Black; + } + base.OnRenderItemText(e); + } + } + } +} + + diff --git a/src/System.Windows.Forms/src/System.Windows.Forms.csproj b/src/System.Windows.Forms/src/System.Windows.Forms.csproj index 7b53c1838d3..1e80900d5e7 100644 --- a/src/System.Windows.Forms/src/System.Windows.Forms.csproj +++ b/src/System.Windows.Forms/src/System.Windows.Forms.csproj @@ -29,6 +29,7 @@ + diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Message.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Message.cs index fb957fdd874..51e312d0a98 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Message.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Message.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - namespace System.Windows.Forms { using System.Text; using System.Runtime.InteropServices; From cd651fedc81640301fc0dd486054aab2762b88cf Mon Sep 17 00:00:00 2001 From: Judit Varsanyi Rozsa Date: Thu, 4 Apr 2019 17:55:28 -0700 Subject: [PATCH 03/10] rebuild resources --- src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf | 6 +++--- src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf | 6 +++--- src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf | 6 +++--- src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf | 6 +++--- src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf | 6 +++--- src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf | 6 +++--- src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf | 6 +++--- src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf | 6 +++--- .../src/Resources/xlf/SR.pt-BR.xlf | 6 +++--- src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf | 6 +++--- src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf | 6 +++--- .../src/Resources/xlf/SR.zh-Hans.xlf | 6 +++--- .../src/Resources/xlf/SR.zh-Hant.xlf | 6 +++--- 13 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf index ad91c4f9d25..c39863ea640 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - Nepovedlo se najít metodu {0}. + Could not find method '{0}'. + Nepovedlo se najít metodu {0}. @@ -1024,4 +1024,4 @@ - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf index 46732429d1e..c1f9461f870 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - {0}-Methode konnte nicht gefunden werden. + Could not find method '{0}'. + {0}-Methode konnte nicht gefunden werden. @@ -1024,4 +1024,4 @@ - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf index 5efc20db21c..0fef8b39c78 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - No se encontró el método "{0}" + Could not find method '{0}'. + No se encontró el método "{0}" @@ -1024,4 +1024,4 @@ - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf index cbad4a58439..2cf5a8609dd 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - Méthode '{0}' introuvable + Could not find method '{0}'. + Méthode '{0}' introuvable @@ -1024,4 +1024,4 @@ - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf index 4abc8b771be..d84d8086900 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - Non è stato possibile trovare il metodo '{0}' + Could not find method '{0}'. + Non è stato possibile trovare il metodo '{0}' @@ -1024,4 +1024,4 @@ - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf index 973406796a0..ad9a9442a08 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - メソッド '{0}' が見つかりませんでした + Could not find method '{0}'. + メソッド '{0}' が見つかりませんでした @@ -1024,4 +1024,4 @@ - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf index 8562d64d23b..ba35ef56491 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - '{0}' 메서드를 찾을 수 없습니다. + Could not find method '{0}'. + '{0}' 메서드를 찾을 수 없습니다. @@ -1024,4 +1024,4 @@ - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf index 6dc112b97c0..a0d3be82ce2 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - Nie można znaleźć metody „{0}”. + Could not find method '{0}'. + Nie można znaleźć metody „{0}”. @@ -1024,4 +1024,4 @@ - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf index 63eb183f4ed..9f4d3f588a4 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - Não foi possível encontrar o método '{0}' + Could not find method '{0}'. + Não foi possível encontrar o método '{0}' @@ -1024,4 +1024,4 @@ - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf index c90e97ff20d..39d88b2bd0b 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - Не удалось найти метод "{0}". + Could not find method '{0}'. + Не удалось найти метод "{0}". @@ -1024,4 +1024,4 @@ - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf index e23af9638f9..ae3e9046c17 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - '{0}' metodu bulunamadı + Could not find method '{0}'. + '{0}' metodu bulunamadı @@ -1024,4 +1024,4 @@ - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf index 6117c2174e1..27b38ff356c 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - 未能找到方法“{0}” + Could not find method '{0}'. + 未能找到方法“{0}” @@ -1024,4 +1024,4 @@ - + \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf index ac4544d74ae..b5d85313613 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf @@ -238,8 +238,8 @@ - Could not find method '{0}' - 找不到方法 '{0}' + Could not find method '{0}'. + 找不到方法 '{0}' @@ -1024,4 +1024,4 @@ - + \ No newline at end of file From a9eea3a9069d0ed48da73aca546c1acdca1ad0fa Mon Sep 17 00:00:00 2001 From: Judit Varsanyi Rozsa Date: Fri, 5 Apr 2019 17:52:34 -0700 Subject: [PATCH 04/10] PR comments --- src/Common/src/CompModSwitches.cs | 43 +- src/Common/src/NativeMethods.cs | 37 +- src/Common/src/SafeNativeMethods.cs | 2 +- .../src/Resources/SR.Designer.cs | 1053 ++++++++++++++--- .../src/Resources/SR.resx | 1 + .../src/Resources/xlf/SR.cs.xlf | 2 +- .../src/Resources/xlf/SR.de.xlf | 2 +- .../src/Resources/xlf/SR.es.xlf | 2 +- .../src/Resources/xlf/SR.fr.xlf | 2 +- .../src/Resources/xlf/SR.it.xlf | 2 +- .../src/Resources/xlf/SR.ja.xlf | 2 +- .../src/Resources/xlf/SR.ko.xlf | 2 +- .../src/Resources/xlf/SR.pl.xlf | 2 +- .../src/Resources/xlf/SR.pt-BR.xlf | 2 +- .../src/Resources/xlf/SR.ru.xlf | 2 +- .../src/Resources/xlf/SR.tr.xlf | 2 +- .../src/Resources/xlf/SR.zh-Hans.xlf | 2 +- .../src/Resources/xlf/SR.zh-Hant.xlf | 2 +- .../Design/DesignerActionHeaderItem.cs | 2 +- .../Design/DesignerActionPanel.cs | 77 +- .../Design/DesignerActionTextItem.cs | 2 +- .../DesignerActionUIStateChangeEventArgs.cs | 14 +- .../Design/DesignerActionUIStateChangeType.cs | 4 +- .../ComponentModel/Design/VsPropertyGrid.cs | 69 -- .../Windows/Forms/Design/Behavior/Adorner.cs | 15 +- .../Forms/Design/Behavior/BehaviorService.cs | 18 +- .../Design/Behavior/ContainerSelectorGlyph.cs | 1 - .../Design/Behavior/DesignerActionBehavior.cs | 6 +- .../Design/Behavior/DesignerActionGlyph.cs | 1 + .../Design/Behavior/DropSourceBehavior.cs | 244 +--- .../Behavior/NoResizeSelectionBorderGlyph.cs | 4 - .../Behavior/SelectionBorderGlyphType.cs | 6 +- .../Design/Behavior/SelectionGlyphBase.cs | 13 +- .../Windows/Forms/Design/Behavior/SnapLine.cs | 3 + .../Windows/Forms/Design/ComponentTray.cs | 47 +- .../ContainerSelectorActiveEventArgs.cs | 4 +- .../ContainerSelectorActiveEventHandler.cs | 4 +- .../Windows/Forms/Design/ControlDesigner.cs | 1 - .../Design/DesignerActionKeyboardBehavior.cs | 54 - .../Forms/Design/DesignerActionService.cs | 37 +- .../Windows/Forms/Design/DesignerActionUI.cs | 6 +- .../Forms/Design/DesignerActionVerbItem.cs | 9 +- .../Forms/Design/DesignerActionVerbList.cs | 6 +- .../Windows/Forms/Design/DesignerFrame.cs | 31 +- .../Forms/Design/SelectionUIService.cs | 299 +++-- .../Forms/Design/ToolStripItemBehavior.cs | 2 +- .../ToolStripItemCustomMenuItemCollection.cs | 1 - .../ToolStripKeyboardHandlingService.cs | 3 +- .../Forms/Design/ToolStripMenuItemDesigner.cs | 22 +- .../Forms/Design/ToolStripTemplateNode.cs | 33 +- 50 files changed, 1184 insertions(+), 1016 deletions(-) delete mode 100644 src/System.Windows.Forms.Design/src/System/ComponentModel/Design/VsPropertyGrid.cs delete mode 100644 src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionKeyboardBehavior.cs diff --git a/src/Common/src/CompModSwitches.cs b/src/Common/src/CompModSwitches.cs index e439e73bc21..f22eaddb3d7 100644 --- a/src/Common/src/CompModSwitches.cs +++ b/src/Common/src/CompModSwitches.cs @@ -7,14 +7,9 @@ namespace System.ComponentModel { using System.Diagnostics; - - /// - // Shared between dlls internal static class CompModSwitches { - #if WINDOWS_FORMS_SWITCHES - private static TraceSwitch activeX; private static TraceSwitch flowLayout; private static TraceSwitch dataCursor; @@ -44,7 +39,11 @@ internal static class CompModSwitches { private static TraceSwitch setBounds; private static BooleanSwitch lifetimeTracing; - + + private static TraceSwitch s_handleLeak; + private static BooleanSwitch s_traceCollect; + private static BooleanSwitch s_commonDesignerServices; + public static TraceSwitch ActiveX { get { if (activeX == null) { @@ -286,8 +285,7 @@ public static TraceSwitch RichLayout { } return richLayout; } - } - + } public static TraceSwitch SetBounds { get { @@ -296,43 +294,36 @@ public static TraceSwitch SetBounds { } return setBounds; } - } - - #endif - - - - private static TraceSwitch handleLeak; + } +#endif public static TraceSwitch HandleLeak { get { - if (handleLeak == null) { - handleLeak = new TraceSwitch("HANDLELEAK", "HandleCollector: Track Win32 Handle Leaks"); + if (s_handleLeak == null) { + s_handleLeak = new TraceSwitch("HANDLELEAK", "HandleCollector: Track Win32 Handle Leaks"); } - return handleLeak; + return s_handleLeak; } } - private static BooleanSwitch traceCollect; public static BooleanSwitch TraceCollect { get { - if (traceCollect == null) { - traceCollect = new BooleanSwitch("TRACECOLLECT", "HandleCollector: Trace HandleCollector operations"); + if (s_traceCollect == null) { + s_traceCollect = new BooleanSwitch("TRACECOLLECT", "HandleCollector: Trace HandleCollector operations"); } - return traceCollect; + return s_traceCollect; } } - private static BooleanSwitch commonDesignerServices; public static BooleanSwitch CommonDesignerServices { get { - if (commonDesignerServices == null) + if (s_commonDesignerServices == null) { - commonDesignerServices = new BooleanSwitch("CommonDesignerServices", "Assert if any common designer service is not found."); + s_commonDesignerServices = new BooleanSwitch("CommonDesignerServices", "Assert if any common designer service is not found."); } - return commonDesignerServices; + return s_commonDesignerServices; } } } diff --git a/src/Common/src/NativeMethods.cs b/src/Common/src/NativeMethods.cs index 6043a30547c..01467f31169 100644 --- a/src/Common/src/NativeMethods.cs +++ b/src/Common/src/NativeMethods.cs @@ -5702,6 +5702,8 @@ public enum MONTCALENDAR_VIEW_MODE public const int PS_GEOMETRIC = 0x00010000; public const int PS_ENDCAP_SQUARE = 0x00000100; + public const int WS_EX_TRANSPARENT = 0x00000020; + public const int NULL_BRUSH = 5; public const int MM_HIMETRIC = 3; @@ -6019,41 +6021,38 @@ public UiaRect(System.Drawing.Rectangle r) { // Windows Server 2003 and Windows XP: This value is not supported. internal const int LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, [In, Out] ref RECT rect, int cPoints); - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, [In, Out] POINT pt, int cPoints); - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern IntPtr WindowFromPoint(int x, int y); - [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); - //[DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] - //[ResourceExposure(ResourceScope.None)] - //public extern static IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, [In, Out] HDHITTESTINFO lParam); - [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam); - [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, string lParam); - [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public extern static IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, [In, Out] TV_HITTESTINFO lParam); - [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern short GetKeyState(int keyCode); - [DllImport(ExternDll.Gdi32, ExactSpelling = true, EntryPoint = "DeleteObject", CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, ExactSpelling = true, EntryPoint = "DeleteObject", CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] private static extern bool IntDeleteObject(IntPtr hObject); public static bool DeleteObject(IntPtr hObject) @@ -6062,15 +6061,15 @@ public static bool DeleteObject(IntPtr hObject) return IntDeleteObject(hObject); } - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern bool GetUpdateRect(IntPtr hwnd, [In, Out] ref RECT rc, bool fErase); - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern bool GetUpdateRgn(IntPtr hwnd, IntPtr hrgn, bool fErase); - [DllImport(ExternDll.Gdi32, ExactSpelling = true, EntryPoint = "CreateRectRgn", CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, ExactSpelling = true, EntryPoint = "CreateRectRgn", CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.Process)] private static extern IntPtr IntCreateRectRgn(int x1, int y1, int x2, int y2); [ResourceExposure(ResourceScope.Process)] @@ -6080,15 +6079,15 @@ public static IntPtr CreateRectRgn(int x1, int y1, int x2, int y2) return System.Internal.HandleCollector.Add(IntCreateRectRgn(x1, y1, x2, y2), CommonHandles.GDI); } - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern IntPtr GetCursor(); - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern bool GetCursorPos([In, Out] POINT pt); - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent); } diff --git a/src/Common/src/SafeNativeMethods.cs b/src/Common/src/SafeNativeMethods.cs index 2b146f7e6c3..bbe978c066e 100644 --- a/src/Common/src/SafeNativeMethods.cs +++ b/src/Common/src/SafeNativeMethods.cs @@ -902,7 +902,7 @@ public static int ColorToCOLORREF(Color color) { return (int)color.R | ((int)color.G << 8) | ((int)color.B << 16); } - [ComImport(), Guid("BEF6E003-A874-101A-8BBA-00AA00300CAB"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIDispatch)] + [ComImport(), Guid("BEF6E003-A874-101A-8BBA-00AA00300CAB"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] public interface IFontDisp { string Name {get; set;} diff --git a/src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs b/src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs index 5d427f81a8a..e96278210cb 100644 --- a/src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs +++ b/src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs @@ -96,6 +96,24 @@ public static string BehaviorServiceMoveControls { } } + /// + /// Looks up a localized string similar to Resize {0}. + /// + public static string BehaviorServiceResizeControl { + get { + return ResourceManager.GetString("BehaviorServiceResizeControl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Resize {0} Controls. + /// + public static string BehaviorServiceResizeControls { + get { + return ResourceManager.GetString("BehaviorServiceResizeControls", resourceCulture); + } + } + /// /// Looks up a localized string similar to Double cannot be converted to a date.. /// @@ -114,6 +132,33 @@ public static string CannotConvertIntToFloat { } } + /// + /// Looks up a localized string similar to The serialization store is closed. New objects cannot be added to a closed store.. + /// + public static string CodeDomComponentSerializationServiceClosedStore { + get { + return ResourceManager.GetString("CodeDomComponentSerializationServiceClosedStore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Complete deserialization of {0} failed.. + /// + public static string CodeDomComponentSerializationServiceDeserializationError { + get { + return ResourceManager.GetString("CodeDomComponentSerializationServiceDeserializationError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This type of serialization store is not supported. Use a store returned by the CreateStore method.. + /// + public static string CodeDomComponentSerializationServiceUnknownStore { + get { + return ResourceManager.GetString("CodeDomComponentSerializationServiceUnknownStore", resourceCulture); + } + } + /// /// Looks up a localized string similar to Unhandled VT: {0}.. /// @@ -153,37 +198,18 @@ public static string CommandSetCutMultiple { /// /// Looks up a localized string similar to Delete {0} components. /// - public static string CommandSetDelete - { - get - { - return ResourceManager.GetString("CommandSetDelete", resourceCulture); - } - } - /// Looks up a localized string similar to The serialization store is closed. New objects cannot be added to a closed store.. - /// - public static string CodeDomComponentSerializationServiceClosedStore { + public static string CommandSetDelete { get { - return ResourceManager.GetString("CodeDomComponentSerializationServiceClosedStore", resourceCulture); + return ResourceManager.GetString("CommandSetDelete", resourceCulture); } } /// /// Looks up a localized string similar to An error occurred while processing this command.\r\n{0}. /// - public static string CommandSetError - { - get - { - return ResourceManager.GetString("CommandSetError", resourceCulture); - } - } - /// Looks up a localized string similar to Complete deserialization of {0} failed.. - /// - public static string CodeDomComponentSerializationServiceDeserializationError { + public static string CommandSetError { get { - return ResourceManager.GetString("CodeDomComponentSerializationServiceDeserializationError", resourceCulture); - + return ResourceManager.GetString("CommandSetError", resourceCulture); } } @@ -195,13 +221,6 @@ public static string CommandSetFormatSpacing { return ResourceManager.GetString("CommandSetFormatSpacing", resourceCulture); } } - /// Looks up a localized string similar to This type of serialization store is not supported. Use a store returned by the CreateStore method.. - /// - public static string CodeDomComponentSerializationServiceUnknownStore { - get { - return ResourceManager.GetString("CodeDomComponentSerializationServiceUnknownStore", resourceCulture); - } - } /// /// Looks up a localized string similar to Paste components. @@ -233,10 +252,8 @@ public static string CommandSetSizeToGrid { /// /// Looks up a localized string similar to Unknown spacing command. /// - public static string CommandSetUnknownSpacingCommand - { - get - { + public static string CommandSetUnknownSpacingCommand { + get { return ResourceManager.GetString("CommandSetUnknownSpacingCommand", resourceCulture); } } @@ -250,6 +267,159 @@ public static string ComponentDesignerAddEvent { } } + /// + /// Looks up a localized string similar to Align To &Grid. + /// + public static string ContextMenuAlignToGrid { + get { + return ResourceManager.GetString("ContextMenuAlignToGrid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Bring To Front. + /// + public static string ContextMenuBringToFront { + get { + return ResourceManager.GetString("ContextMenuBringToFront", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to C&opy. + /// + public static string ContextMenuCopy { + get { + return ResourceManager.GetString("ContextMenuCopy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Cut. + /// + public static string ContextMenuCut { + get { + return ResourceManager.GetString("ContextMenuCut", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Delete. + /// + public static string ContextMenuDelete { + get { + return ResourceManager.GetString("ContextMenuDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Document Outline. + /// + public static string ContextMenuDocumentOutline { + get { + return ResourceManager.GetString("ContextMenuDocumentOutline", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Lock Controls. + /// + public static string ContextMenuLockControls { + get { + return ResourceManager.GetString("ContextMenuLockControls", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Paste. + /// + public static string ContextMenuPaste { + get { + return ResourceManager.GetString("ContextMenuPaste", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Properties. + /// + public static string ContextMenuProperties { + get { + return ResourceManager.GetString("ContextMenuProperties", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Select. + /// + public static string ContextMenuSelect { + get { + return ResourceManager.GetString("ContextMenuSelect", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Send To Back. + /// + public static string ContextMenuSendToBack { + get { + return ResourceManager.GetString("ContextMenuSendToBack", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ShowCheckMargin. + /// + public static string ContextMenuStripActionList_ShowCheckMargin { + get { + return ResourceManager.GetString("ContextMenuStripActionList_ShowCheckMargin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toggles the ShowCheckMargin property. + /// + public static string ContextMenuStripActionList_ShowCheckMarginDesc { + get { + return ResourceManager.GetString("ContextMenuStripActionList_ShowCheckMarginDesc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ShowImageMargin. + /// + public static string ContextMenuStripActionList_ShowImageMargin { + get { + return ResourceManager.GetString("ContextMenuStripActionList_ShowImageMargin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toggles the ShowImageMargin property. + /// + public static string ContextMenuStripActionList_ShowImageMarginDesc { + get { + return ResourceManager.GetString("ContextMenuStripActionList_ShowImageMarginDesc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to View &Code. + /// + public static string ContextMenuViewCode { + get { + return ResourceManager.GetString("ContextMenuViewCode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2}. + /// + public static string ControlDesigner_WndProcException { + get { + return ResourceManager.GetString("ControlDesigner_WndProcException", resourceCulture); + } + } + /// /// Looks up a localized string similar to Could not convert value '{0}' to the type '{1}'.. /// @@ -260,7 +430,7 @@ public static string DesignerActionPanel_CouldNotConvertValue { } /// - /// Looks up a localized string similar to Could not find method '{0}'. + /// Looks up a localized string similar to Could not find method '{0}'.. /// public static string DesignerActionPanel_CouldNotFindMethod { get { @@ -610,6 +780,24 @@ public static string DesignerOptions_UseSnapLines { } } + /// + /// Looks up a localized string similar to Dock in Parent Container. + /// + public static string DesignerShortcutDockInParent { + get { + return ResourceManager.GetString("DesignerShortcutDockInParent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Undock in Parent Container. + /// + public static string DesignerShortcutUndockInParent { + get { + return ResourceManager.GetString("DesignerShortcutUndockInParent", resourceCulture); + } + } + /// /// Looks up a localized string similar to The container cannot be disposed at design time.. /// @@ -676,13 +864,32 @@ public static string DotNET_ComponentType { /// /// Looks up a localized string similar to Drag {0} components. /// - public static string DragDropDragComponents - { - get - { + public static string DragDropDragComponents { + get { return ResourceManager.GetString("DragDropDragComponents", resourceCulture); } - } /// Looks up a localized string similar to This IDataObject doesn't support SetData.. + } + + /// + /// Looks up a localized string similar to Move {0}. + /// + public static string DragDropMoveComponent { + get { + return ResourceManager.GetString("DragDropMoveComponent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Move {0} components. + /// + public static string DragDropMoveComponents { + get { + return ResourceManager.GetString("DragDropMoveComponents", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This IDataObject doesn't support SetData.. /// public static string DragDropSetDataError { get { @@ -690,6 +897,24 @@ public static string DragDropSetDataError { } } + /// + /// Looks up a localized string similar to Size {0}. + /// + public static string DragDropSizeComponent { + get { + return ResourceManager.GetString("DragDropSizeComponent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size {0} components. + /// + public static string DragDropSizeComponents { + get { + return ResourceManager.GetString("DragDropSizeComponents", resourceCulture); + } + } + /// /// Looks up a localized string similar to The extender provider {0} has already been added as an extender. Adding another would result in duplicate properties.. /// @@ -700,15 +925,7 @@ public static string ExtenderProviderServiceDuplicateProvider { } /// - /// Looks up a localized string similar to Move {0}. - /// - public static string DragDropMoveComponent - { - get - { - return ResourceManager.GetString("DragDropMoveComponent", resourceCulture); - } - } /// Looks up a localized string similar to Read-Only. + /// Looks up a localized string similar to Read-Only. /// public static string InheritanceServiceReadOnlyCollection { get { @@ -717,15 +934,34 @@ public static string InheritanceServiceReadOnlyCollection { } /// - /// Looks up a localized string similar to Move {0} components. + /// Looks up a localized string similar to '{1}' is not a valid value for '{0}'.. /// - public static string DragDropMoveComponents - { - get - { - return ResourceManager.GetString("DragDropMoveComponents", resourceCulture); + public static string InvalidArgument { + get { + return ResourceManager.GetString("InvalidArgument", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '{1}' is not a valid value for '{0}'. '{0}' should be between {2} and {3}.. + /// + public static string InvalidBoundArgument { + get { + return ResourceManager.GetString("InvalidBoundArgument", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The Locked property determines if we can move or resize the control.. + /// + public static string lockedDescr { + get { + return ResourceManager.GetString("lockedDescr", resourceCulture); } - } /// Looks up a localized string similar to This method/object is not implemented by design.. + } + + /// + /// Looks up a localized string similar to This method/object is not implemented by design.. /// public static string NotImplementedByDesign { get { @@ -733,7 +969,7 @@ public static string NotImplementedByDesign { } } - /// + /// /// Looks up a localized string similar to Winforms Designer is not supported on this platform.. /// public static string PlatformNotSupported_WinformsDesigner { @@ -743,15 +979,16 @@ public static string PlatformNotSupported_WinformsDesigner { } /// - /// Looks up a localized string similar to Size {0}. + /// Looks up a localized string similar to RTL_False. /// - public static string DragDropSizeComponent - { - get - { - return ResourceManager.GetString("DragDropSizeComponent", resourceCulture); + public static string RTL { + get { + return ResourceManager.GetString("RTL", resourceCulture); } - } /// Looks up a localized string similar to You cannot create a new session because this serialization manager already has an active serialization session.. + } + + /// + /// Looks up a localized string similar to You cannot create a new session because this serialization manager already has an active serialization session.. /// public static string SerializationManagerAreadyInSession { get { @@ -760,15 +997,7 @@ public static string SerializationManagerAreadyInSession { } /// - /// Looks up a localized string similar to Size {0} components. - /// - public static string DragDropSizeComponents - { - get - { - return ResourceManager.GetString("DragDropSizeComponents", resourceCulture); - } - } /// Looks up a localized string similar to Duplicate declaration of member '{0}'. + /// Looks up a localized string similar to Duplicate declaration of member '{0}'. /// public static string SerializationManagerDuplicateComponentDecl { get { @@ -809,94 +1038,46 @@ public static string SerializationManagerNoSession { public static string SerializationManagerObjectHasName { get { return ResourceManager.GetString("SerializationManagerObjectHasName", resourceCulture); - } } /// - /// Looks up a localized string similar to RTL_False. - /// - public static string RTL - { - get - { - return ResourceManager.GetString("RTL", resourceCulture); - } - } /// Looks up a localized string similar to This method cannot be invoked because the serialization manager has an active serialization session.. /// public static string SerializationManagerWithinSession { get { return ResourceManager.GetString("SerializationManagerWithinSession", resourceCulture); - } } /// - - /// Looks up a localized string similar to Argument should be a non-empty string.. - /// - public static string ToolboxItemInvalidKey { - get { - return ResourceManager.GetString("ToolboxItemInvalidKey", resourceCulture); - } - } /// Looks up a localized string similar to Elements of type {0} are not supported. The serializer expects the element to be one of the following: {1}.. /// public static string SerializerBadElementTypes { get { return ResourceManager.GetString("SerializerBadElementTypes", resourceCulture); - } } /// - /// Looks up a localized string similar to Property {0} requires an argument of type {1}.. - /// - public static string ToolboxItemInvalidPropertyType - { - get - { - return ResourceManager.GetString("ToolboxItemInvalidPropertyType", resourceCulture); - } - } /// Looks up a localized string similar to The field '{0}' could not be found on the target object. Make sure that the field is defined as an instance variable on the target object and has the correct scope.. /// public static string SerializerFieldTargetEvalFailed { get { return ResourceManager.GetString("SerializerFieldTargetEvalFailed", resourceCulture); - } } /// - - /// Looks up a localized string similar to Toolbox item cannot be modified.. - /// - public static string ToolboxItemLocked { - get { - return ResourceManager.GetString("ToolboxItemLocked", resourceCulture); - } - } - /// Looks up a localized string similar to Array rank '{0}' is too high. Visual Studio can only save and load arrays with a rank of 1.. /// public static string SerializerInvalidArrayRank { get { return ResourceManager.GetString("SerializerInvalidArrayRank", resourceCulture); - } } /// - - /// Looks up a localized string similar to Data type {0} is not serializable. Items added to a property dictionary must be serializable.. - /// - public static string ToolboxItemValueNotSerializable { - get { - return ResourceManager.GetString("ToolboxItemValueNotSerializable", resourceCulture); - } - } /// Looks up a localized string similar to Code statements for the object '{0}' were lost during serialization. This may have been a result of another object misbehaving during serialization.. /// public static string SerializerLostStatements { @@ -924,15 +1105,6 @@ public static string SerializerNoRootExpression { } /// - /// Looks up a localized string similar to Auto Arrange Tray Icons. - /// - public static string TrayAutoArrange - { - get - { - return ResourceManager.GetString("TrayAutoArrange", resourceCulture); - } - } /// Looks up a localized string similar to The object '{0}' failed to serialize itself. It may not support code generation.. /// public static string SerializerNoSerializerForComponent { @@ -942,50 +1114,24 @@ public static string SerializerNoSerializerForComponent { } /// - - /// Looks up a localized string similar to Line Up Tray Icons. - /// - public static string TrayLineUpIcons { - get { - return ResourceManager.GetString("TrayLineUpIcons", resourceCulture); - } - } /// Looks up a localized string similar to The type '{0}' has no event named '{1}'.. /// public static string SerializerNoSuchEvent { get { return ResourceManager.GetString("SerializerNoSuchEvent", resourceCulture); - } } /// - - /// Looks up a localized string similar to Show Large or Small Icons. - /// - public static string TrayShowLargeIcons { - get { - return ResourceManager.GetString("TrayShowLargeIcons", resourceCulture); - } -} /// Looks up a localized string similar to The type '{0}' has no field named '{1}'.. /// public static string SerializerNoSuchField { get { return ResourceManager.GetString("SerializerNoSuchField", resourceCulture); - } } /// - - /// Looks up a localized string similar to Error. - /// - public static string UIServiceHelper_ErrorCaption { - get { - return ResourceManager.GetString("UIServiceHelper_ErrorCaption", resourceCulture); - } - } /// Looks up a localized string similar to The type '{0}' has no property named '{1}'.. /// public static string SerializerNoSuchProperty { @@ -1030,13 +1176,606 @@ public static string SerializerUndeclaredName { } } + /// + /// Looks up a localized string similar to &About.... + /// + public static string StandardMenuAbout { + get { + return ResourceManager.GetString("StandardMenuAbout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Contents. + /// + public static string StandardMenuContents { + get { + return ResourceManager.GetString("StandardMenuContents", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Copy. + /// + public static string StandardMenuCopy { + get { + return ResourceManager.GetString("StandardMenuCopy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create Standard Menu. + /// + public static string StandardMenuCreateDesc { + get { + return ResourceManager.GetString("StandardMenuCreateDesc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Customize. + /// + public static string StandardMenuCustomize { + get { + return ResourceManager.GetString("StandardMenuCustomize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cu&t. + /// + public static string StandardMenuCut { + get { + return ResourceManager.GetString("StandardMenuCut", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Edit. + /// + public static string StandardMenuEdit { + get { + return ResourceManager.GetString("StandardMenuEdit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to E&xit. + /// + public static string StandardMenuExit { + get { + return ResourceManager.GetString("StandardMenuExit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &File. + /// + public static string StandardMenuFile { + get { + return ResourceManager.GetString("StandardMenuFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Help. + /// + public static string StandardMenuHelp { + get { + return ResourceManager.GetString("StandardMenuHelp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Index. + /// + public static string StandardMenuIndex { + get { + return ResourceManager.GetString("StandardMenuIndex", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &New. + /// + public static string StandardMenuNew { + get { + return ResourceManager.GetString("StandardMenuNew", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Open. + /// + public static string StandardMenuOpen { + get { + return ResourceManager.GetString("StandardMenuOpen", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Options. + /// + public static string StandardMenuOptions { + get { + return ResourceManager.GetString("StandardMenuOptions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Paste. + /// + public static string StandardMenuPaste { + get { + return ResourceManager.GetString("StandardMenuPaste", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Print. + /// + public static string StandardMenuPrint { + get { + return ResourceManager.GetString("StandardMenuPrint", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Print Pre&view. + /// + public static string StandardMenuPrintPreview { + get { + return ResourceManager.GetString("StandardMenuPrintPreview", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Redo. + /// + public static string StandardMenuRedo { + get { + return ResourceManager.GetString("StandardMenuRedo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Save. + /// + public static string StandardMenuSave { + get { + return ResourceManager.GetString("StandardMenuSave", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save &As. + /// + public static string StandardMenuSaveAs { + get { + return ResourceManager.GetString("StandardMenuSaveAs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Search. + /// + public static string StandardMenuSearch { + get { + return ResourceManager.GetString("StandardMenuSearch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select &All. + /// + public static string StandardMenuSelectAll { + get { + return ResourceManager.GetString("StandardMenuSelectAll", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Tools. + /// + public static string StandardMenuTools { + get { + return ResourceManager.GetString("StandardMenuTools", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Undo. + /// + public static string StandardMenuUndo { + get { + return ResourceManager.GetString("StandardMenuUndo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to C&ut. + /// + public static string StandardToolCut { + get { + return ResourceManager.GetString("StandardToolCut", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to He&lp. + /// + public static string StandardToolHelp { + get { + return ResourceManager.GetString("StandardToolHelp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Argument should be a non-empty string.. + /// + public static string ToolboxItemInvalidKey { + get { + return ResourceManager.GetString("ToolboxItemInvalidKey", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Property {0} requires an argument of type {1}.. + /// + public static string ToolboxItemInvalidPropertyType { + get { + return ResourceManager.GetString("ToolboxItemInvalidPropertyType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toolbox item cannot be modified.. + /// + public static string ToolboxItemLocked { + get { + return ResourceManager.GetString("ToolboxItemLocked", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Data type {0} is not serializable. Items added to a property dictionary must be serializable.. + /// + public static string ToolboxItemValueNotSerializable { + get { + return ResourceManager.GetString("ToolboxItemValueNotSerializable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dock:. + /// + public static string ToolStripActionList_Dock { + get { + return ResourceManager.GetString("ToolStripActionList_Dock", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Changes the Dock property. + /// + public static string ToolStripActionList_DockDesc { + get { + return ResourceManager.GetString("ToolStripActionList_DockDesc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to GripStyle:. + /// + public static string ToolStripActionList_GripStyle { + get { + return ResourceManager.GetString("ToolStripActionList_GripStyle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Changes the GripStyle property. + /// + public static string ToolStripActionList_GripStyleDesc { + get { + return ResourceManager.GetString("ToolStripActionList_GripStyleDesc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Layout && Appearance. + /// + public static string ToolStripActionList_Layout { + get { + return ResourceManager.GetString("ToolStripActionList_Layout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to RenderMode:. + /// + public static string ToolStripActionList_RenderMode { + get { + return ResourceManager.GetString("ToolStripActionList_RenderMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Changes the RenderMode property. + /// + public static string ToolStripActionList_RenderModeDesc { + get { + return ResourceManager.GetString("ToolStripActionList_RenderModeDesc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Adding {0} Item. + /// + public static string ToolStripAddingItem { + get { + return ResourceManager.GetString("ToolStripAddingItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to AllowItemReorder and AllowDrop cannot both be true.. + /// + public static string ToolStripAllowItemReorderAndAllowDropCannotBeSetToTrue { + get { + return ResourceManager.GetString("ToolStripAllowItemReorderAndAllowDropCannotBeSetToTrue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ToolStrip New Item create Transaction.. + /// + public static string ToolStripCreatingNewItemTransaction { + get { + return ResourceManager.GetString("ToolStripCreatingNewItemTransaction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Embed in ToolStripContainer. + /// + public static string ToolStripDesignerEmbedVerb { + get { + return ResourceManager.GetString("ToolStripDesignerEmbedVerb", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Embeds the current ToolStrip in ToolStripContainer. + /// + public static string ToolStripDesignerEmbedVerbDesc { + get { + return ResourceManager.GetString("ToolStripDesignerEmbedVerbDesc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Insert Standard Items. + /// + public static string ToolStripDesignerStandardItemsVerb { + get { + return ResourceManager.GetString("ToolStripDesignerStandardItemsVerb", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Inserts standard items in the current ToolStrip. + /// + public static string ToolStripDesignerStandardItemsVerbDesc { + get { + return ResourceManager.GetString("ToolStripDesignerStandardItemsVerbDesc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type Here. + /// + public static string ToolStripDesignerTemplateNodeEnterText { + get { + return ResourceManager.GetString("ToolStripDesignerTemplateNodeEnterText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type Text for ToolStripMenuItem. + /// + public static string ToolStripDesignerTemplateNodeLabelToolTip { + get { + return ResourceManager.GetString("ToolStripDesignerTemplateNodeLabelToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add Menu Item. + /// + public static string ToolStripDesignerTemplateNodeSplitButtonStatusStripAccessibleName { + get { + return ResourceManager.GetString("ToolStripDesignerTemplateNodeSplitButtonStatusStripAccessibleName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add ToolStripStatusLabel. + /// + public static string ToolStripDesignerTemplateNodeSplitButtonStatusStripToolTip { + get { + return ResourceManager.GetString("ToolStripDesignerTemplateNodeSplitButtonStatusStripToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add ToolStripButton. + /// + public static string ToolStripDesignerTemplateNodeSplitButtonToolTip { + get { + return ResourceManager.GetString("ToolStripDesignerTemplateNodeSplitButtonToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New item selection. + /// + public static string ToolStripDesignerToolStripAccessibleName { + get { + return ResourceManager.GetString("ToolStripDesignerToolStripAccessibleName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Adding Item. + /// + public static string ToolStripDesignerTransactionAddingItem { + get { + return ResourceManager.GetString("ToolStripDesignerTransactionAddingItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Removing Item. + /// + public static string ToolStripDesignerTransactionRemovingItem { + get { + return ResourceManager.GetString("ToolStripDesignerTransactionRemovingItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Edit DropDownItems.... + /// + public static string ToolStripDropDownItemCollectionEditorVerb { + get { + return ResourceManager.GetString("ToolStripDropDownItemCollectionEditorVerb", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ToolStrip MenuItem Insert in DropDown Transaction.. + /// + public static string ToolStripInsertingIntoDropDownTransaction { + get { + return ResourceManager.GetString("ToolStripInsertingIntoDropDownTransaction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Edit Items.... + /// + public static string ToolStripItemCollectionEditorVerb { + get { + return ResourceManager.GetString("ToolStripItemCollectionEditorVerb", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Con&vert To. + /// + public static string ToolStripItemContextMenuConvertTo { + get { + return ResourceManager.GetString("ToolStripItemContextMenuConvertTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to &Insert. + /// + public static string ToolStripItemContextMenuInsert { + get { + return ResourceManager.GetString("ToolStripItemContextMenuInsert", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set I&mage.... + /// + public static string ToolStripItemContextMenuSetImage { + get { + return ResourceManager.GetString("ToolStripItemContextMenuSetImage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ToolStripItem Property Change Transaction.. + /// + public static string ToolStripItemPropertyChangeTransaction { + get { + return ResourceManager.GetString("ToolStripItemPropertyChangeTransaction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ToolStripItem Morphing Transaction.. + /// + public static string ToolStripMorphingItemTransaction { + get { + return ResourceManager.GetString("ToolStripMorphingItemTransaction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '{0}'. + /// + public static string ToolStripSelectMenuItem { + get { + return ResourceManager.GetString("ToolStripSelectMenuItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot add ToolStripSeparator to MenuStrip.. + /// + public static string ToolStripSeparatorError { + get { + return ResourceManager.GetString("ToolStripSeparatorError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Auto Arrange Tray Icons. + /// + public static string TrayAutoArrange { + get { + return ResourceManager.GetString("TrayAutoArrange", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Line Up Tray Icons. + /// + public static string TrayLineUpIcons { + get { + return ResourceManager.GetString("TrayLineUpIcons", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show Large or Small Icons. + /// + public static string TrayShowLargeIcons { + get { + return ResourceManager.GetString("TrayShowLargeIcons", resourceCulture); + } + } + /// /// Looks up a localized string similar to Type '{0}' is not available in the target framework.. /// public static string TypeNotFoundInTargetFramework { get { return ResourceManager.GetString("TypeNotFoundInTargetFramework", resourceCulture); - + } + } + + /// + /// Looks up a localized string similar to Error. + /// + public static string UIServiceHelper_ErrorCaption { + get { + return ResourceManager.GetString("UIServiceHelper_ErrorCaption", resourceCulture); } } diff --git a/src/System.Windows.Forms.Design/src/Resources/SR.resx b/src/System.Windows.Forms.Design/src/Resources/SR.resx index 939018ba43d..a1937f1a1d7 100644 --- a/src/System.Windows.Forms.Design/src/Resources/SR.resx +++ b/src/System.Windows.Forms.Design/src/Resources/SR.resx @@ -284,6 +284,7 @@ RTL_False + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. Auto Arrange Tray Icons diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf index 2c59f7724f6..5a3e626d131 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf index 5eb694b6704..aa425ac0607 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf index 0498c403dd5..3f90ffdd4ac 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf index 3fe25387d22..e0ea65218c8 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf index fd4a76c7d73..605341c2592 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf index 7bc93550b96..01f28346e5e 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf index 9629740113c..4e27eb632af 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf index 37535c629d0..296d8b61648 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf index 4ff20c5c140..82f2d325dd9 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf index 341059f515b..29cc199c306 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf index e2ff83ce063..4e2250fbfb6 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf index 1f3b3522e83..a3aa665cbd7 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf index 7c3ba0c44ae..d7afeb357f9 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf @@ -510,7 +510,7 @@ RTL_False RTL_False - + RTL_False in Left-to-right languages and anything else (RTL_True) for right to left. You cannot create a new session because this serialization manager already has an active serialization session. diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionHeaderItem.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionHeaderItem.cs index 029ac2137d5..860ca085afe 100644 --- a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionHeaderItem.cs +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionHeaderItem.cs @@ -4,7 +4,7 @@ namespace System.ComponentModel.Design { - public sealed class DesignerActionHeaderItem : DesignerActionTextItem + internal sealed class DesignerActionHeaderItem : DesignerActionTextItem { public DesignerActionHeaderItem(string displayName) : base(displayName, displayName) { diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs index 5b3ec89386a..6dba5de5dc0 100644 --- a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs @@ -23,8 +23,6 @@ namespace System.ComponentModel.Design { internal sealed class DesignerActionPanel : ContainerControl { - public const string ExternDllGdi32 = "gdi32.dll"; - public const string ExternDllUser32 = "user32.dll"; private static readonly object s_eventFormActivated = new object(); private static readonly object s_eventFormDeactivate = new object(); @@ -67,9 +65,6 @@ internal sealed class DesignerActionPanel : ContainerControl private readonly IServiceProvider _serviceProvider; private bool _inMethodInvoke; -#if MVWASSEMBLY - private bool _inPushingValue; -#endif private bool _updatingTasks; private bool _dropDownActive; @@ -231,17 +226,6 @@ public bool InMethodInvoke internal set => _inMethodInvoke = value; } -#if MVWASSEMBLY - public bool InPushingValue { - get { - return _inPushingValue; - } - internal set { - _inPushingValue = value; - } - } -#endif - public Color LinkColor { get => _linkColor; @@ -516,8 +500,6 @@ private void OnFormClosing(object sender, CancelEventArgs e) Form form = (Form)TopLevelControl; if (form != null) { - form.Activated -= new EventHandler(OnFormActivated); - form.Deactivate -= new EventHandler(OnFormDeactivate); form.Closing -= new CancelEventHandler(OnFormClosing); } } @@ -533,8 +515,6 @@ protected override void OnHandleCreated(EventArgs e) base.OnHandleCreated(e); if (TopLevelControl is Form form) { - form.Activated += new EventHandler(OnFormActivated); - form.Deactivate += new EventHandler(OnFormDeactivate); form.Closing += new CancelEventHandler(OnFormClosing); } } @@ -1179,7 +1159,6 @@ private sealed class PanelHeaderLine : Line public PanelHeaderLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) : base(serviceProvider, actionPanel) { - actionPanel.FontChanged += new EventHandler(OnParentControlFontChanged); } public sealed override string FocusId @@ -1207,9 +1186,6 @@ protected override void AddControls(List controls) controls.Add(_titleLabel); controls.Add(_subtitleLabel); - // TODO: Need to figure out how to unhook these events. Perhaps have Initialize() and Cleanup() methods. - ActionPanel.FormActivated += new EventHandler(OnFormActivated); - ActionPanel.FormDeactivate += new EventHandler(OnFormDeactivate); } public sealed override void Focus() @@ -1460,10 +1436,6 @@ protected void SetValue(object newValue) return; } _pushingValue = true; - -#if MVWASSEMBLY - ActionPanel.InPushingValue = true; -#endif try { // Only push the change if the values are different @@ -1507,9 +1479,6 @@ protected void SetValue(object newValue) finally { _pushingValue = false; -#if MVWASSEMBLY - ActionPanel.InPushingValue = false; -#endif } } @@ -1522,9 +1491,6 @@ internal sealed override void UpdateActionItem(DesignerActionList actionList, De _value = PropertyDescriptor.GetValue(actionList); OnPropertyTaskItemUpdated(toolTip, ref currentTabIndex); _pushingValue = true; -#if MVWASSEMBLY - ActionPanel.InPushingValue = true; -#endif try { OnValueChanged(); @@ -1532,9 +1498,6 @@ internal sealed override void UpdateActionItem(DesignerActionList actionList, De finally { _pushingValue = false; -#if MVWASSEMBLY - ActionPanel.InPushingValue = false; -#endif } } } @@ -1554,7 +1517,6 @@ protected override void AddControls(List controls) BackColor = Color.Transparent, CheckAlign = Drawing.ContentAlignment.MiddleLeft }; - _checkBox.CheckedChanged += new EventHandler(OnCheckBoxCheckedChanged); _checkBox.TextAlign = Drawing.ContentAlignment.MiddleLeft; _checkBox.UseMnemonic = false; _checkBox.ForeColor = ActionPanel.LabelForeColor; @@ -1649,8 +1611,6 @@ protected override void AddControls(List controls) Visible = false }; _readOnlyTextBoxLabel.MouseClick += new MouseEventHandler(OnReadOnlyTextBoxLabelClick); - _readOnlyTextBoxLabel.Enter += new EventHandler(OnReadOnlyTextBoxLabelEnter); - _readOnlyTextBoxLabel.Leave += new EventHandler(OnReadOnlyTextBoxLabelLeave); _readOnlyTextBoxLabel.KeyDown += new KeyEventHandler(OnReadOnlyTextBoxLabelKeyDown); _textBox = new TextBox @@ -1659,9 +1619,7 @@ protected override void AddControls(List controls) TextAlign = System.Windows.Forms.HorizontalAlignment.Left, Visible = false }; - _textBox.TextChanged += new EventHandler(OnTextBoxTextChanged); _textBox.KeyDown += new KeyEventHandler(OnTextBoxKeyDown); - _textBox.LostFocus += new EventHandler(OnTextBoxLostFocus); controls.Add(_readOnlyTextBoxLabel); controls.Add(_textBox); @@ -1984,7 +1942,6 @@ private void ActivateDropDown() IntegralHeight = false, Font = ActionPanel.Font }; - listBox.SelectedIndexChanged += new EventHandler(OnListBoxSelectedIndexChanged); listBox.KeyDown += new KeyEventHandler(OnListBoxKeyDown); TypeConverter.StandardValuesCollection standardValues = GetStandardValues(); if (standardValues != null) @@ -2040,7 +1997,6 @@ private void ActivateDropDown() } finally { - listBox.SelectedIndexChanged -= new EventHandler(OnListBoxSelectedIndexChanged); listBox.KeyDown -= new KeyEventHandler(OnListBoxKeyDown); } @@ -2058,8 +2014,6 @@ protected override void AddControls(List controls) { base.AddControls(controls); _button = new EditorButton(); - _button.Click += new EventHandler(OnButtonClick); - _button.GotFocus += new EventHandler(OnButtonGotFocus); controls.Add(_button); } @@ -2451,11 +2405,7 @@ public void DoModalLoop() { while (Visible) { -#if MVWASSEMBLY - System.Windows.Forms.Application.DoEvents(); -#else Application.DoEvents(); -#endif UnsafeNativeMethods.MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 250, NativeMethods.QS_ALLINPUT, NativeMethods.MWMO_INPUTAVAILABLE); } } @@ -2660,7 +2610,7 @@ public struct TEXTMETRICA private static class SafeNativeMethods { - [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, EntryPoint = "DeleteObject", CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, EntryPoint = "DeleteObject", CharSet = System.Runtime.InteropServices.CharSet.Auto)] private static extern bool IntDeleteObject(HandleRef hObject); public static bool DeleteObject(HandleRef hObject) { @@ -2668,19 +2618,19 @@ public static bool DeleteObject(HandleRef hObject) return IntDeleteObject(hObject); } - [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] public static extern bool ReleaseCapture(); - [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] public static extern IntPtr SelectObject(HandleRef hDC, HandleRef hObject); - [DllImport(ExternDllGdi32, SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [DllImport(ExternDll.Gdi32, SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] public static extern int GetTextExtentPoint32(HandleRef hDC, string str, int len, [In, Out] NativeMethods.SIZE size); - [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] public static extern int GetTextMetricsW(HandleRef hDC, [In, Out] ref NativeMethods.TEXTMETRIC lptm); - [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi)] + [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi)] public static extern int GetTextMetricsA(HandleRef hDC, [In, Out] ref NativeMethods.TEXTMETRICA lptm); public static int GetTextMetrics(HandleRef hDC, ref NativeMethods.TEXTMETRIC lptm) @@ -2722,25 +2672,25 @@ public static int GetTextMetrics(HandleRef hDC, ref NativeMethods.TEXTMETRIC lpt private static class UnsafeNativeMethods { - [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] public static extern IntPtr GetWindowLong(HandleRef hWnd, int nIndex); - [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] public static extern IntPtr SetWindowLong(HandleRef hWnd, int nIndex, HandleRef dwNewLong); - [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] public static extern int MsgWaitForMultipleObjectsEx(int nCount, IntPtr pHandles, int dwMilliseconds, int dwWakeMask, int dwFlags); [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] - [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam); - [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] public static extern IntPtr GetCapture(); - [DllImport(ExternDllUser32, ExactSpelling = true, EntryPoint = "GetDC", CharSet = CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, EntryPoint = "GetDC", CharSet = CharSet.Auto)] private static extern IntPtr IntGetDC(HandleRef hWnd); public static IntPtr GetDC(HandleRef hWnd) { @@ -2748,7 +2698,7 @@ public static IntPtr GetDC(HandleRef hWnd) return IntGetDC(hWnd); } - [DllImport(ExternDllUser32, ExactSpelling = true, EntryPoint = "ReleaseDC", CharSet = CharSet.Auto)] + [DllImport(ExternDll.User32, ExactSpelling = true, EntryPoint = "ReleaseDC", CharSet = CharSet.Auto)] private static extern int IntReleaseDC(HandleRef hWnd, HandleRef hDC); public static int ReleaseDC(HandleRef hWnd, HandleRef hDC) { @@ -2926,7 +2876,6 @@ private class TextLine : Line public TextLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) : base(serviceProvider, actionPanel) { - actionPanel.FontChanged += new EventHandler(OnParentControlFontChanged); } public sealed override string FocusId diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionTextItem.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionTextItem.cs index 0f8c90cb7d7..61607c58347 100644 --- a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionTextItem.cs +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionTextItem.cs @@ -4,7 +4,7 @@ namespace System.ComponentModel.Design { - public class DesignerActionTextItem : DesignerActionItem + internal class DesignerActionTextItem : DesignerActionItem { public DesignerActionTextItem(string displayName, string category) : base(displayName, category, null) { diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventArgs.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventArgs.cs index 47e19a70b04..96e1bf46d99 100644 --- a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventArgs.cs +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventArgs.cs @@ -10,10 +10,10 @@ namespace System.ComponentModel.Design public class DesignerActionUIStateChangeEventArgs : EventArgs { private readonly object _relatedObject; - private readonly DesignerActionUIStateChangeType _changeType; //type of change + private readonly DesignerActionUIStateChangeType _changeType; /// - /// Constructor that requires the object in question, the type of change and the remaining actionlists left for the object. on the related object. + /// Constructor that requires the object in question, the type of change and the remaining actionlists left for the object on the related object. /// public DesignerActionUIStateChangeEventArgs(object relatedObject, DesignerActionUIStateChangeType changeType) { @@ -24,17 +24,11 @@ public DesignerActionUIStateChangeEventArgs(object relatedObject, DesignerAction /// /// The type of changed that caused the related event to be thrown. /// - public DesignerActionUIStateChangeType ChangeType - { - get => _changeType; - } + public DesignerActionUIStateChangeType ChangeType => _changeType; /// /// The object this change is related to. /// - public object RelatedObject - { - get => _relatedObject; - } + public object RelatedObject => _relatedObject; } } diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeType.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeType.cs index e4f35eed025..ed9edc4cec5 100644 --- a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeType.cs +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeType.cs @@ -6,8 +6,6 @@ namespace System.ComponentModel.Design { public enum DesignerActionUIStateChangeType { - Show, - Hide, - Refresh + Show, Hide, Refresh } } diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/VsPropertyGrid.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/VsPropertyGrid.cs deleted file mode 100644 index 85c09db2fb5..00000000000 --- a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/VsPropertyGrid.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Drawing; -using System.IO; -using System.Windows.Forms; - -namespace System.ComponentModel.Design -{ - internal class VsPropertyGrid : PropertyGrid - { - private static readonly Size s_iCON_SIZE = new Size(16, 16); - private static Size s_iconSize = s_iCON_SIZE; - private static bool s_isScalingInitialized = false; - - public VsPropertyGrid(IServiceProvider serviceProvider) : base() - { - } - - protected override Bitmap SortByPropertyImage - { - get => GetBitmap("PBAlpha"); - } - - protected override Bitmap SortByCategoryImage - { - get => GetBitmap("PBCatego", true); - } - - protected override Bitmap ShowPropertyPageImage - { - get => GetBitmap("PBPPage"); - } - - // try to find the best possible image - private Bitmap GetBitmap(string resourceName, bool setMagentaTransparent = false) - { - // this resource might be present in System.Windows.Forms.VisualStudio.15.0.dll if this code is running on dev14 or newer - Stream stream = BitmapSelector.GetResourceStream(typeof(PropertyGrid), resourceName + ".ico"); - Bitmap bitmap; - if (stream != null) - { - if (!VsPropertyGrid.s_isScalingInitialized) - { - if (DpiHelper.IsScalingRequired) - { - VsPropertyGrid.s_iconSize = DpiHelper.LogicalToDeviceUnits(s_iCON_SIZE); - } - VsPropertyGrid.s_isScalingInitialized = true; - } - // retrieve icon closest to the desired size - Icon icon = new Icon(stream, VsPropertyGrid.s_iconSize); - bitmap = icon.ToBitmap(); - icon.Dispose(); - } - else - { - // this resource must be present in System.Windows.Forms.dll if it is not available in System.Windows.Forms.VisualStudio.15.0.dll - bitmap = new Bitmap(BitmapSelector.GetResourceStream(typeof(PropertyGrid), resourceName + ".bmp")); - if (setMagentaTransparent) - { - bitmap.MakeTransparent(Color.Magenta); - } - } - return bitmap; - } - } -} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/Adorner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/Adorner.cs index 87d597cca99..33734fea269 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/Adorner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/Adorner.cs @@ -16,7 +16,6 @@ public sealed class Adorner { private BehaviorService _behaviorService; //ptr back to the BehaviorService private readonly GlyphCollection _glyphs; //collection of Glyphs that this particular Adorner manages - private bool _enabled; //enabled value - determines if Adorner gets paints & hits /// /// Standard constructor. Creates a new GlyphCollection and by default is enabled. @@ -24,7 +23,7 @@ public sealed class Adorner public Adorner() { _glyphs = new GlyphCollection(); - _enabled = true; + EnabledInternal = true; } /// @@ -53,11 +52,8 @@ public bool Enabled } } } - internal bool EnabledInternal - { - get => _enabled; - set => _enabled = value; - } + + internal bool EnabledInternal { get; set; } /// /// Returns the stronly-typed Glyph collection. @@ -73,10 +69,7 @@ public GlyphCollection Glyphs /// public void Invalidate() { - if (_behaviorService != null) - { - _behaviorService.Invalidate(); - } + _behaviorService?.Invalidate(); } /// diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs index 0ae6485f571..1451e4a7e07 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs @@ -71,7 +71,7 @@ public sealed class BehaviorService : IDisposable internal BehaviorService(IServiceProvider serviceProvider, Control windowFrame) { _serviceProvider = serviceProvider; -//create the AdornerWindow + //create the AdornerWindow _adornerWindow = new AdornerWindow(this, windowFrame); //use the adornerWindow as an overlay @@ -94,14 +94,9 @@ internal BehaviorService(IServiceProvider serviceProvider, Control windowFrame) _trackingMouseEvent = false; //create out object that will handle all menucommands - IMenuCommandService menuCommandService = serviceProvider.GetService(typeof(IMenuCommandService)) as IMenuCommandService; - IDesignerHost host = serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; - - if (menuCommandService != null && host != null) + if (serviceProvider.GetService(typeof(IMenuCommandService)) is IMenuCommandService menuCommandService && serviceProvider.GetService(typeof(IDesignerHost)) is IDesignerHost host) { - _menuCommandHandler = new MenuCommandHandler(this, menuCommandService); - host.RemoveService(typeof(IMenuCommandService)); host.AddService(typeof(IMenuCommandService), _menuCommandHandler); } @@ -115,8 +110,6 @@ internal BehaviorService(IServiceProvider serviceProvider, Control windowFrame) WM_GETRECENTSNAPLINES = SafeNativeMethods.RegisterWindowMessage("WM_GETRECENTSNAPLINES"); // Listen to the SystemEvents so that we can resync selection based on display settings etc. - SystemEvents.DisplaySettingsChanged += new EventHandler(OnSystemSettingChanged); - SystemEvents.InstalledFontsChanged += new EventHandler(OnSystemSettingChanged); SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); } /// @@ -240,8 +233,6 @@ public void Dispose() } _adornerWindow.Dispose(); - SystemEvents.DisplaySettingsChanged -= new EventHandler(OnSystemSettingChanged); - SystemEvents.InstalledFontsChanged -= new EventHandler(OnSystemSettingChanged); SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); } @@ -558,7 +549,7 @@ public void PushBehavior(Behavior behavior) { if (behavior == null) { - throw new ArgumentNullException("behavior"); + throw new ArgumentNullException(nameof(behavior)); } // Should we catch this @@ -657,7 +648,7 @@ protected override CreateParams CreateParams { CreateParams cp = base.CreateParams; cp.Style &= ~(NativeMethods.WS_CLIPCHILDREN | NativeMethods.WS_CLIPSIBLINGS); - cp.ExStyle |= 0x00000020/*WS_EX_TRANSPARENT*/; + cp.ExStyle |= NativeMethods.WS_EX_TRANSPARENT; return cp; } } @@ -1760,6 +1751,7 @@ private void TestHook_GetAllSnapLines(ref Message m) } TestHook_SetText(ref m, snapLineInfo); } + private void OnDragOver(DragEventArgs e) { // cache off our validDragArgs so we can re-fabricate enter/leave drag events diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorGlyph.cs index 9776723833d..7012cc0b1e0 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorGlyph.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorGlyph.cs @@ -22,7 +22,6 @@ internal ContainerSelectorGlyph(Rectangle containerBounds, int glyphSize, int gl _relatedBehavior = (ContainerSelectorBehavior)behavior; _glyphBounds = new Rectangle(containerBounds.X + glyphOffset, containerBounds.Y - (int)(glyphSize * .5), glyphSize, glyphSize); } - /// /// The bounds of this Glyph. diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionBehavior.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionBehavior.cs index 5fd4a883d5a..c5dce31b979 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionBehavior.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionBehavior.cs @@ -16,9 +16,9 @@ namespace System.Windows.Forms.Design.Behavior /// internal sealed class DesignerActionBehavior : Behavior { - private readonly IComponent _relatedComponent;//The component we are bound to - private DesignerActionUI _parentUI;//ptr to the parenting UI, used for showing menus and setting selection - private DesignerActionListCollection _actionLists;//all the shortcuts! + private readonly IComponent _relatedComponent; //The component we are bound to + private readonly DesignerActionUI _parentUI; //ptr to the parenting UI, used for showing menus and setting selection + private DesignerActionListCollection _actionLists; //all the shortcuts! private readonly IServiceProvider _serviceProvider; // we need to cache the service provider here to be able to create the panel with the proper arguments private bool _ignoreNextMouseUp = false; diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionGlyph.cs index a5f72397385..70a2bc447dc 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionGlyph.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionGlyph.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; +using System.ComponentModel.Design; using System.Drawing; namespace System.Windows.Forms.Design.Behavior diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DropSourceBehavior.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DropSourceBehavior.cs index 7a17bab8548..7ccc19a5d71 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DropSourceBehavior.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DropSourceBehavior.cs @@ -164,119 +164,6 @@ private Point AdjustToGrid(Point dragLoc) return offset; } -#if PERFORM_AUTO_MARGINS - -/* Leaving in in case we want to re-enable this feature. The code as it is does presents major performance problems. */ - /// - /// Called after a successful drag/drop operation, this method will examine the position - /// of all the controls related (same parent) to the just-dragged control and determine - /// if they were placed within the recommended UI guidelines. If so, we'll attempt to - /// automatically adjust the margin/padding of the violators. - /// - private void AutoAdjustMargins(Control dragControl) { - if (dragControl.Parent == null) { - return; - } - - PropertyDescriptor marginProperty = TypeDescriptor.GetProperties(dragControl)["Margin"]; - //TODO: should we always adjust margins - or just when autorelocate is on??? JeffChri and FredB will think about this - //PropertyDescriptor autoRelocateProperty = TypeDescriptor.GetProperties(dragControl)["AutoRelocate"]; - if (marginProperty == null /*|| autoRelocateProperty == null*/) { - //can't do anything here - return; - } - - IDesignerHost designerHost = serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; - if (designerHost == null) { - Debug.Fail("Failed to get IDesignerHost!"); - return; - } - - IComponentChangeService changeService = serviceProvider.GetService(typeof(IComponentChangeService)) as IComponentChangeService; - - Control.ControlCollection controls = dragControl.Parent.Controls; - //loop through the controls attempting to identify which controls - //are violating others... - for (int i = 0; i < controls.Count - 1; i++) { - /* See TODO above - //control must have auto-relocate set to true - if (!(bool)autoRelocateProperty.GetValue(controls[i])) { - continue; - } - */ - for (int j = i + 1; j < controls.Count; j++) { - /* See TODO above - //this control must also have auto-relocate set to true - if (!(bool)autoRelocateProperty.GetValue(controls[j])) { - continue; - } - */ - - //check if control i and control j are violating one another... - if (DoesViolateMargin(controls[i], controls[j])) { - - //determine the new margins - Padding c1Margin = Padding.Empty; - Padding c2Margin = Padding.Empty; - SetMargins(controls[i], controls[j], ref c1Margin, ref c2Margin); - - //set the margins - using (DesignerTransaction dt = designerHost.CreateTransaction(SR.GetString(SR.AutoAdjustMargins, controls[i].Site.Name, controls[j].Site.Name))) { - if (changeService != null) { - changeService.OnComponentChanging(controls[i], marginProperty); - changeService.OnComponentChanging(controls[j], marginProperty); - } - marginProperty.SetValue(controls[i], c1Margin); - marginProperty.SetValue(controls[j], c2Margin); - - if (changeService != null) { - changeService.OnComponentChanged(controls[i], marginProperty, null, null); - changeService.OnComponentChanged(controls[j], marginProperty, null, null); - } - dt.Commit(); - } - - } - } - } - - } - - /// - /// This function determines where the two controls margins are overlapped and - // fills up two Padding structs with suggested non-overlapped values. - /// - private void SetMargins(Control c1, Control c2, ref Padding c1Margin, ref Padding c2Margin) { - //Now, perform the actual margin adjustments - // - if (c1.Bottom < c2.Top) { - //adjust the margins @ top of c1 and bottom of c2 - int marginDelta = c2.Top - c1.Bottom; - c1Margin = new Padding(c1.Margin.Left, c1.Margin.Top, c1.Margin.Right, marginDelta / 2); - c2Margin = new Padding(c2.Margin.Left, marginDelta - c1Margin.Bottom, c2.Margin.Right, c2.Margin.Bottom); - } - else if (c1.Top > c2.Bottom) { - //adjust the margins @ bottom of c2and top of c1 - int marginDelta = c1.Top - c2.Bottom; - c1Margin = new Padding(c1.Margin.Left, marginDelta / 2, c1.Margin.Right, c1.Margin.Bottom); - c2Margin = new Padding(c2.Margin.Left, c2.Margin.Top, c2.Margin.Right, marginDelta - c1Margin.Top); - } - else if (c1.Right < c2.Left) { - //adjust the margins @ left of c2and right of c1 - int marginDelta = c2.Left - c1.Right; - c1Margin = new Padding(c1.Margin.Left, c1.Margin.Top, marginDelta / 2, c1.Margin.Bottom); - c2Margin = new Padding(marginDelta - c1Margin.Right, c2.Margin.Top, c2.Margin.Right, c2.Margin.Bottom); - } - else { - //adjust the margins @ right of c2 and left of c1 - int marginDelta = c1.Left - c2.Right; - c1Margin = new Padding(marginDelta / 2, c1.Margin.Top, c1.Margin.Right , c1.Margin.Bottom); - c2Margin = new Padding(c2.Margin.Left, c2.Margin.Top, marginDelta - c1Margin.Left, c2.Margin.Bottom); - } - - } -#endif - private Point MapPointFromSourceToTarget(Point pt) { if (srcHost != destHost && destHost != null) @@ -331,101 +218,6 @@ private void ClearAllDragImages() } } -#if PERFORM_AUTO_ANCHOR - -/* Leaving in in case we want to re-enable this feature. The code as it is does presents major usability problems as - it changes anchors out from underneath you. -*/ - /// - /// This method is called after a drag operation has been completed. - /// Basically, the heuristic is: if we've just moved a control (only 1 - /// at a time) and it is aligned with another control - then check - /// the anchoring properties. If the dragged control's anchoring is - /// default - and the aligned control's anchoring has been altered - /// then set the dragged control to mimic the aligned control's - /// anchor value. - /// - private void PerformAutoAnchor(object dragControl) { - Control dragCtrl = dragControl as Control; - - if (dragCtrl == null || dragCtrl.Parent == null) { - //dragged object is not a control or there is no valid panent - return; - } - - PropertyDescriptor dragControlAnchorProp = TypeDescriptor.GetProperties(dragCtrl)["Anchor"]; - if (dragControlAnchorProp == null) { - //couldn't get anhor prop - return; - } - - DefaultValueAttribute defAttr = (DefaultValueAttribute)dragControlAnchorProp.Attributes[typeof(DefaultValueAttribute)]; - if (defAttr != null && (AnchorStyles)defAttr.Value != (AnchorStyles)dragControlAnchorProp.GetValue(dragCtrl)) { - //dragged control's anchor prop was not default - return; - } - - AnchorStyles dragControlAnchor = (AnchorStyles)dragControlAnchorProp.GetValue(dragCtrl); - Rectangle dragControlBounds = dragCtrl.Bounds; - - //begin a search to find a control the dragControl is aligned with. We'll - //stop looking as soon as we find a control that aligns with us and have a - //different anchor value - foreach (Control c in dragCtrl.Parent.Controls) { - if (c.Equals(dragCtrl)) { - //skip if this is the dragged control - continue; - } - - Rectangle bounds = c.Bounds; - - //look for a friendly aligned child control - if (c.Left == dragControlBounds.Left || c.Right == dragControlBounds.Right || - c.Top == dragControlBounds.Top || c.Bottom == dragControlBounds.Bottom) { - - PropertyDescriptor anchorProp = TypeDescriptor.GetProperties(c)["Anchor"]; - if (anchorProp != null) { - //cache off the anchorstyle for this aligned child control - AnchorStyles style = (AnchorStyles)anchorProp.GetValue(c); - - if (style != dragControlAnchor) { - - //here, the aligned child control's anchor prop is - //different from our dragcontrol! So we'll create a transaction - //and set the dragControl to the same style. - IDesignerHost host = serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; - if (host == null) { - //couldn't find a designer host - return; - } - - using (DesignerTransaction dt = host.CreateTransaction(SR.GetString(SR.PerformAutoAnchor, c.Site.Name, dragCtrl.Site.Name))) { - dragControlAnchorProp.SetValue(dragControl, style); - dt.Commit(); - break; - } - } - } - } - } - } - -#endif - -#if PERFORM_AUTO_MARGINS - /// - /// Returns true if c1 and c2 are within each others margins - /// but not overlapping. - /// - private bool DoesViolateMargin(Control c1, Control c2) { - //if the margin rects intersect and the and the actual - //control bounds do not - then there's a violation - // - return !(Rectangle.Intersect(DesignerUtils.GetMarginBounds(c1), DesignerUtils.GetMarginBounds(c2)).IsEmpty) && - (Rectangle.Intersect(c1.Bounds, c2.Bounds).IsEmpty); - } -#endif - // Yeah this is recursive, but we also need to resite all // the children of this control, and their children, and their children... private void SetDesignerHost(Control c) @@ -539,9 +331,6 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) } } -#if PERFORM_AUTO_STUFF - bool successfulDrag = false; -#endif // We use this list when doing a Drag-Copy, so that we can correctly restore state when we are done. See Copy code below. ArrayList originalControls = null; bool performCopy = (lastEffect == DragDropEffects.Copy); @@ -558,7 +347,7 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) dragAssistanceManager.OnMouseUp(); } - // If we are dropping between hosts, we want to set the selection in the new host to be the components that we are dropping. VSWhidbey# 395676 ... or if we are copying + // If we are dropping between hosts, we want to set the selection in the new host to be the components that we are dropping. ... or if we are copying ISelectionService selSvc = null; if (performCopy || (srcHost != destHost && destHost != null)) { @@ -753,9 +542,6 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) { transSource.Commit(); transSource = null; -#if PERFORM_AUTO_STUFF - successfulDrag = true; -#endif } if (transTarget != null) { @@ -799,27 +585,6 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) } } -#if PERFORM_AUTO_STUFF - - if (successfulDrag) { - -#if PERFORM_AUTO_MARGINS - //auto adjust margin values of controls if they were - //placed within the recommended UI distances... - AutoAdjustMargins(dragComponents[primaryComponentIndex].dragComponent as Control); -#endif - -#if PERFORM_AUTO_ANCHOR - - //if we aligned a single control with another that has - //non-default anchoring props, then we will apply those - //values to the control that we just dragged. - if (dragComponents.Length == 1) { - PerformAutoAnchor(dragComponents[0].dragComponent); - } -#endif - } -#endif // clear the last feedback loc lastFeedbackLocation = new Point(-1, -1); } @@ -1033,12 +798,7 @@ internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) { invalidDragRegion.Translate(mouseLoc.X - initialMouseLoc.X + lastSnapOffset.X, mouseLoc.Y - initialMouseLoc.Y + lastSnapOffset.Y); invalidDragRegion.Complement(newImageRect); - invalidDragRegion.Union(invalidRegion); -#if DEBUGDROPSOURCE - System.Threading.Thread.Sleep(750); - graphicsTarget.FillRegion(Brushes.Red, invalidDragRegion); - System.Threading.Thread.Sleep(750); -#endif + invalidDragRegion.Union(invalidRegion); behaviorServiceTarget.Invalidate(invalidDragRegion); } invalidRegion.Dispose(); diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeSelectionBorderGlyph.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeSelectionBorderGlyph.cs index 73a26c77587..03cd2fe226e 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeSelectionBorderGlyph.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeSelectionBorderGlyph.cs @@ -56,9 +56,6 @@ private void InitializeGlyph(Rectangle controlBounds, SelectionRules selRules, S hitBounds.Width += DesignerUtils.SELECTIONBORDERHITAREA - DesignerUtils.SELECTIONBORDERSIZE; break; } - - - } /// @@ -68,6 +65,5 @@ public override void Paint(PaintEventArgs pe) { DesignerUtils.DrawSelectionBorder(pe.Graphics, bounds); } - } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionBorderGlyphType.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionBorderGlyphType.cs index b0936fdd918..bfb30c1be2a 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionBorderGlyphType.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionBorderGlyphType.cs @@ -9,10 +9,6 @@ namespace System.Windows.Forms.Design.Behavior /// internal enum SelectionBorderGlyphType { - Top, - Bottom, - Left, - Right, - Body + Top, Bottom, Left, Right, Body } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionGlyphBase.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionGlyphBase.cs index 3913a68f1fc..766efd2c361 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionGlyphBase.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionGlyphBase.cs @@ -11,10 +11,10 @@ namespace System.Windows.Forms.Design.Behavior /// internal abstract class SelectionGlyphBase : Glyph { - protected Rectangle bounds;//defines the bounds of the selection glyph - protected Rectangle hitBounds;//defines the bounds used for hittest - it could be different than the bounds of the glyph itself - protected Cursor hitTestCursor;//the cursor returned if hit test is positive - protected SelectionRules rules;//the selection rules - defining how the control can change + protected Rectangle bounds; // defines the bounds of the selection glyph + protected Rectangle hitBounds; // defines the bounds used for hittest - it could be different than the bounds of the glyph itself + protected Cursor hitTestCursor; // the cursor returned if hit test is positive + protected SelectionRules rules; // the selection rules - defining how the control can change /// /// Standard constructor. @@ -42,7 +42,7 @@ public override Cursor GetHitTest(Point p) } return null; } - + /// /// Returns the HitTestCursor for this glyph. /// @@ -50,11 +50,10 @@ public Cursor HitTestCursor { get => hitTestCursor; } - + /// /// The Bounds of this glyph. /// - public override Rectangle Bounds { get => bounds; diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SnapLine.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SnapLine.cs index 5725b6750d8..9a635338181 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SnapLine.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SnapLine.cs @@ -18,6 +18,9 @@ namespace System.Windows.Forms.Design.Behavior /// public sealed class SnapLine { + // These are used in the SnapLine filter to define custom margin/padding SnapLines. + // Margins will have special rules of equality, basically opposites will attract one another + // (ex: margin right == margin left) and paddings will be attracted to like-margins. internal const string Margin = "Margin"; internal const string MarginRight = Margin + ".Right"; internal const string MarginLeft = Margin + ".Left"; diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ComponentTray.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ComponentTray.cs index cd85da2e5a9..7f1a426da2e 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ComponentTray.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ComponentTray.cs @@ -38,7 +38,7 @@ public class ComponentTray : ScrollableControl, IExtenderProvider, ISelectionUIH private IToolboxService toolboxService; // cached for drag/drop /// - /// Provides drag and drop functionality through OLE. + /// Provides drag and drop functionality through OLE. /// internal OleDragDropHandler oleDragDropHandler; // handler class for ole drag drop operations. @@ -173,9 +173,6 @@ public ComponentTray(IDesigner mainDesigner, IServiceProvider serviceProvider) SystemEvents.InstalledFontsChanged += new EventHandler(OnSystemSettingChanged); SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); - // Listen to refresh events from TypeDescriptor. If a component gets refreshed, we re-query and will hide/show the view based on the DesignerView attribute. - TypeDescriptor.Refreshed += new RefreshEventHandler(OnComponentRefresh); - if (GetService(typeof(BehaviorService)) is BehaviorService behSvc) { //this object will manage any glyphs that get added to our tray @@ -435,9 +432,7 @@ public bool AutoArrange } /// - /// /// Gets the number of compnents contained within this tray. - /// /// public int ComponentCount { @@ -671,7 +666,7 @@ private void OnContextMenu(int x, int y, bool useSelection) void ISelectionUIHandler.OleDragLeave() => GetOleDragHandler().DoOleDragLeave(); /// - /// Adds a component to the tray. + /// Adds a component to the tray. /// public virtual void AddComponent(IComponent component) { @@ -836,17 +831,14 @@ protected void DisplayError(Exception e) } /// - /// /// Disposes of the resources (other than memory) used by the component tray object. - /// /// protected override void Dispose(bool disposing) { if (disposing && controls != null) { IExtenderProviderService es = (IExtenderProviderService)GetService(typeof(IExtenderProviderService)); - if (CompModSwitches.CommonDesignerServices.Enabled) - Debug.Assert(es != null, "IExtenderProviderService not found"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (es != null), "IExtenderProviderService not found"); if (es != null) { es.RemoveExtenderProvider(this); @@ -868,7 +860,6 @@ protected override void Dispose(bool disposing) componentChangeService.ComponentRemoved -= new ComponentEventHandler(OnComponentRemoved); } - TypeDescriptor.Refreshed -= new RefreshEventHandler(OnComponentRefresh); SystemEvents.DisplaySettingsChanged -= new EventHandler(OnSystemSettingChanged); SystemEvents.InstalledFontsChanged -= new EventHandler(OnSystemSettingChanged); SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); @@ -993,9 +984,7 @@ public Point GetTrayLocation(IComponent receiver) } /// - /// /// Gets the requsted service type. - /// /// protected override object GetService(Type serviceType) { @@ -1040,8 +1029,7 @@ protected override void OnMouseDoubleClick(MouseEventArgs e) { OnLostCapture(); IEventBindingService eps = (IEventBindingService)GetService(typeof(IEventBindingService)); - if (CompModSwitches.CommonDesignerServices.Enabled) - Debug.Assert(eps != null, "IEventBindingService not found"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (eps != null), "IEventBindingService not found"); if (eps != null) { eps.ShowCode(); @@ -1072,8 +1060,7 @@ protected override void OnDragDrop(DragEventArgs de) { ToolboxItem tool = mouseDragTool; mouseDragTool = null; - if (CompModSwitches.CommonDesignerServices.Enabled) - Debug.Assert(GetService(typeof(IDesignerHost)) != null, "IDesignerHost not found"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (GetService(typeof(IDesignerHost)) != null), "IDesignerHost not found"); try { IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); @@ -1268,8 +1255,7 @@ protected override void OnMouseDown(MouseEventArgs e) try { ISelectionService ss = (ISelectionService)GetService(typeof(ISelectionService)); - if (CompModSwitches.CommonDesignerServices.Enabled) - Debug.Assert(ss != null, "ISelectionService not found"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (ss != null), "ISelectionService not found"); if (ss != null) { ss.SetSelectedComponents(new object[] { mainDesigner.Component }); @@ -1362,8 +1348,7 @@ protected override void OnMouseUp(MouseEventArgs e) try { ISelectionService ss = (ISelectionService)GetService(typeof(ISelectionService)); - if (CompModSwitches.CommonDesignerServices.Enabled) - Debug.Assert(ss != null, "ISelectionService not found"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (ss != null), "ISelectionService not found"); if (ss != null) { ss.SetSelectedComponents(comps); @@ -1934,8 +1919,7 @@ public TrayControl(ComponentTray tray, IComponent component) UpdateIconInfo(); IComponentChangeService cs = (IComponentChangeService)tray.GetService(typeof(IComponentChangeService)); - if (CompModSwitches.CommonDesignerServices.Enabled) - Debug.Assert(cs != null, "IComponentChangeService not found"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (cs != null), "IComponentChangeService not found"); if (cs != null) { cs.ComponentRename += new ComponentRenameEventHandler(OnComponentRename); @@ -2047,16 +2031,14 @@ protected override void Dispose(bool disposing) if (site != null) { IComponentChangeService cs = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); - if (CompModSwitches.CommonDesignerServices.Enabled) - Debug.Assert(cs != null, "IComponentChangeService not found"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (cs != null), "IComponentChangeService not found"); if (cs != null) { cs.ComponentRename -= new ComponentRenameEventHandler(OnComponentRename); } IDictionaryService ds = (IDictionaryService)site.GetService(typeof(IDictionaryService)); - if (CompModSwitches.CommonDesignerServices.Enabled) - Debug.Assert(ds != null, "IDictionaryService not found"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (ds != null), "IDictionaryService not found"); if (ds != null) { ds.SetValue(typeof(TrayControl), null); @@ -2081,8 +2063,7 @@ public static TrayControl FromComponent(IComponent component) if (site != null) { IDictionaryService ds = (IDictionaryService)site.GetService(typeof(IDictionaryService)); - if (CompModSwitches.CommonDesignerServices.Enabled) - Debug.Assert(ds != null, "IDictionaryService not found"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (ds != null), "IDictionaryService not found"); if (ds != null) { c = (TrayControl)ds.GetValue(typeof(TrayControl)); @@ -2192,8 +2173,7 @@ protected override void OnMouseDown(MouseEventArgs me) { ISelectionService sel = (ISelectionService)_tray.GetService(typeof(ISelectionService)); // Make sure the component is selected - if (CompModSwitches.CommonDesignerServices.Enabled) - Debug.Assert(sel != null, "ISelectionService not found"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (sel != null), "ISelectionService not found"); if (sel != null) { sel.SetSelectedComponents(new object[] { Component }, SelectionTypes.Primary); @@ -2499,8 +2479,7 @@ public virtual void ViewDefaultEvent(IComponent component) PropertyDescriptor defaultPropEvent = null; bool eventChanged = false; IEventBindingService eps = (IEventBindingService)GetService(typeof(IEventBindingService)); - if (CompModSwitches.CommonDesignerServices.Enabled) - Debug.Assert(eps != null, "IEventBindingService not found"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (eps != null), "IEventBindingService not found"); if (eps != null) { defaultPropEvent = eps.GetEventProperty(defaultEvent); diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventArgs.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventArgs.cs index b0aa7f18f70..7c1af36b0b2 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventArgs.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventArgs.cs @@ -5,7 +5,7 @@ namespace System.Windows.Forms.Design { /// - /// Provides data for the event. + /// Provides data for the event. /// internal class ContainerSelectorActiveEventArgs : EventArgs { @@ -26,6 +26,6 @@ public ContainerSelectorActiveEventArgs(object component, ContainerSelectorActiv { _component = component; _eventType = eventType; - } + } } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventHandler.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventHandler.cs index d896603bd09..39d645db344 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventHandler.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventHandler.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Forms.Design.ContainerSelectorActiveEventHandler..ctor(System.Object,System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Forms.Design.ContainerSelectorActiveEventHandler..ctor(System.Object,System.IntPtr)")] namespace System.Windows.Forms.Design { /// diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs index 229f68c8368..0f9fad51de2 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs @@ -1320,7 +1320,6 @@ protected virtual void OnMouseDragEnd(bool cancel) Control.Capture = false; if (!_mouseDragMoved) { - // HACK HACK HACK // ParentControlDesigner.Dispose depends on cancel having this behavior. if (!cancel) { diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionKeyboardBehavior.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionKeyboardBehavior.cs deleted file mode 100644 index 9ba0a827644..00000000000 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionKeyboardBehavior.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.ComponentModel.Design; -using System.Diagnostics; -using System.Windows.Forms.Design.Behavior; - -namespace System.Windows.Forms.Design -{ - internal sealed class DesignerActionKeyboardBehavior : Behavior.Behavior - { - private DesignerActionPanel _panel; - private readonly IMenuCommandService _menuService; - private readonly DesignerActionUIService _daUISvc; - private static readonly Guid s_vSStandardCommandSet97 = new Guid("{5efc7975-14bc-11cf-9b2b-00aa00573819}"); - - public DesignerActionKeyboardBehavior(DesignerActionPanel panel, IServiceProvider serviceProvider, BehaviorService behaviorService) : base(true, behaviorService) - { - _panel = panel; - if (serviceProvider != null) - { - _menuService = serviceProvider.GetService(typeof(IMenuCommandService)) as IMenuCommandService; - Debug.Assert(_menuService != null, "we should have found a menu service here..."); - _daUISvc = serviceProvider.GetService(typeof(DesignerActionUIService)) as DesignerActionUIService; - } - } - // THIS shoudl not stay here, creation of a custom command or of the real thing should be handled in the - // designeractionpanel itself - public override MenuCommand FindCommand(CommandID commandId) - { - if (_panel != null && _menuService != null) - { - // if the command we're looking for is handled by the panel, just tell VS that this command is disabled. otherwise let it through as usual... - foreach (CommandID candidateCommandId in _panel.FilteredCommandIDs) - { - // VisualStudio shell implements a mutable derived class from the base CommandID. The mutable class compares overridden properties instead of the read-only backing fields when testing equality of command IDs. Thus Equals method is asymmetrical when comparing VS implementation against the .Net implementation and the derived class's override that compares properties is the accurate one. - if (commandId.Equals(candidateCommandId)) - { - MenuCommand dummyMC = new MenuCommand(delegate { }, commandId); - dummyMC.Enabled = false; - return dummyMC; - } - } - // in case of a ctrl-tab we need to close the DAP - if (_daUISvc != null && commandId.Guid == DesignerActionKeyboardBehavior.s_vSStandardCommandSet97 && commandId.ID == 1124) - { - _daUISvc.HideUI(null); - } - } - return base.FindCommand(commandId); // this will route the request to the parent behavior - } - } -} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionService.cs index 48a5fd3d5a6..99dab6ee00c 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionService.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionService.cs @@ -3,11 +3,10 @@ // See the LICENSE file in the project root for more information. using System.Collections; -using System.ComponentModel; -using System.ComponentModel.Design; using System.Diagnostics; +using System.Windows.Forms.Design; -namespace System.Windows.Forms.Design +namespace System.ComponentModel.Design { /// /// The DesignerActionService manages DesignerActions. All DesignerActions are associated with an object. DesignerActions can be added or removed at any given time. The DesignerActionService controls the expiration of DesignerActions by monitoring three basic events: selection change, component change, and timer expiration. Designer implementing this service will need to monitor the DesignerActionsChanged event on this class. This event will fire every time a change is made to any object's DesignerActions. @@ -23,7 +22,7 @@ public class DesignerActionService : IDisposable private bool _reEntrantCode = false; /// - /// Standard constructor. A Service Provider is necessary for monitoring selection and component changes. + /// Standard constructor. A Service Provider is necessary for monitoring selection and component changes. /// public DesignerActionService(IServiceProvider serviceProvider) { @@ -63,11 +62,11 @@ public void Add(IComponent comp, DesignerActionListCollection designerActionList { if (comp == null) { - throw new ArgumentNullException("comp"); + throw new ArgumentNullException(nameof(comp)); } if (designerActionListCollection == null) { - throw new ArgumentNullException("designerActionListCollection"); + throw new ArgumentNullException(nameof(designerActionListCollection)); } DesignerActionListCollection dhlc = (DesignerActionListCollection)_designerActionLists[comp]; @@ -85,8 +84,8 @@ public void Add(IComponent comp, DesignerActionListCollection designerActionList } /// - /// Adds a new DesignerActionList to be monitored - /// with the related comp object + /// Adds a new DesignerActionList to be monitored with the related comp object + /// public void Add(IComponent comp, DesignerActionList actionList) { Add(comp, new DesignerActionListCollection( new[] { actionList } )); @@ -126,7 +125,7 @@ public bool Contains(IComponent comp) { if (comp == null) { - throw new ArgumentNullException("comp"); + throw new ArgumentNullException(nameof(comp)); } return _designerActionLists.Contains(comp); } @@ -166,7 +165,7 @@ public virtual DesignerActionListCollection GetComponentActions(IComponent compo { if (component == null) { - throw new ArgumentNullException("component"); + throw new ArgumentNullException(nameof(component)); } DesignerActionListCollection result = new DesignerActionListCollection(); switch (type) @@ -189,12 +188,12 @@ protected virtual void GetComponentDesignerActions(IComponent component, Designe { if (component == null) { - throw new ArgumentNullException("component"); + throw new ArgumentNullException(nameof(component)); } if (actionLists == null) { - throw new ArgumentNullException("actionLists"); + throw new ArgumentNullException(nameof(actionLists)); } if (component.Site is IServiceContainer sc) @@ -293,12 +292,12 @@ protected virtual void GetComponentServiceActions(IComponent component, Designer { if (component == null) { - throw new ArgumentNullException("component"); + throw new ArgumentNullException(nameof(component)); } if (actionLists == null) { - throw new ArgumentNullException("actionLists"); + throw new ArgumentNullException(nameof(actionLists)); } DesignerActionListCollection pushCollection = (DesignerActionListCollection)_designerActionLists[component]; @@ -340,7 +339,7 @@ public void Remove(IComponent comp) { if (comp == null) { - throw new ArgumentNullException("comp"); + throw new ArgumentNullException(nameof(comp)); } if (!_designerActionLists.Contains(comp)) @@ -359,7 +358,7 @@ public void Remove(DesignerActionList actionList) { if (actionList == null) { - throw new ArgumentNullException("actionList"); + throw new ArgumentNullException(nameof(actionList)); } //find the associated component @@ -374,17 +373,17 @@ public void Remove(DesignerActionList actionList) } /// - /// This will remove the all instances of the DesignerAction from the 'comp' object. If an alarm was set, it will be unhooked. This will also fire the DesignerActionChanged event. + /// This will remove the all instances of the DesignerAction from the 'comp' object. If an alarm was set, it will be unhooked. This will also fire the DesignerActionChanged event. /// public void Remove(IComponent comp, DesignerActionList actionList) { if (comp == null) { - throw new ArgumentNullException("comp"); + throw new ArgumentNullException(nameof(comp)); } if (actionList == null) { - throw new ArgumentNullException("actionList"); + throw new ArgumentNullException(nameof(actionList)); } if (!_designerActionLists.Contains(comp)) { diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUI.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUI.cs index 017bf221a66..27dc6afc343 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUI.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUI.cs @@ -3,15 +3,15 @@ // See the LICENSE file in the project root for more information. using System.Collections; -using System.ComponentModel; -using System.ComponentModel.Design; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices; using System.Text; +using System.Windows.Forms; +using System.Windows.Forms.Design; using System.Windows.Forms.Design.Behavior; -namespace System.Windows.Forms.Design +namespace System.ComponentModel.Design { /// /// The DesignerActionUI is the designer/UI-specific implementation of the DesignerActions feature. This class instantiates the DesignerActionService and hooks to its DesignerActionsChanged event. Responding to this single event will enable the DesignerActionUI to perform all neceessary UI-related operations. Note that the DesignerActionUI uses the BehaviorService to manage all UI interaction. For every component containing a DesignerAction (determined by the DesignerActionsChagned event) there will be an associated DesignerActionGlyph and DesignerActionBehavior. Finally, the DesignerActionUI is also responsible for showing and managing the Action's context menus. Note that every DesignerAction context menu has an item that will bring up the DesignerActions option pane in the options dialog. diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbItem.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbItem.cs index ba57b6eb8c6..d771812b3e0 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbItem.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbItem.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.ComponentModel.Design; - -namespace System.Windows.Forms.Design +namespace System.ComponentModel.Design { internal class DesignerActionVerbItem : DesignerActionMethodItem { @@ -20,10 +18,7 @@ public override string Category get => "Verbs"; } - public override string Description - { - get => _targetVerb.Description; - } + public override string Description { get; } public override string DisplayName { diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbList.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbList.cs index 6eef6dd68f8..6ef7876dfe0 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbList.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionVerbList.cs @@ -2,13 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.ComponentModel.Design; - -namespace System.Windows.Forms.Design +namespace System.ComponentModel.Design { internal class DesignerActionVerbList : DesignerActionList { - private DesignerVerb[] _verbs; + private readonly DesignerVerb[] _verbs; public DesignerActionVerbList(DesignerVerb[] verbs) : base(null) { diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerFrame.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerFrame.cs index 040c5b254e7..c150dca91cc 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerFrame.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerFrame.cs @@ -17,11 +17,11 @@ namespace System.Windows.Forms.Design /// /// This class implements our design time document. This is the outer window that encompases a designer. It maintains a control hierarchy that looks like this: /// DesignerFrame - /// ScrollableControl - /// Designer - /// Splitter - /// ScrollableControl - /// Component Tray + /// ScrollableControl + /// Designer + /// Splitter + /// ScrollableControl + /// Component Tray /// The splitter and second scrollable control are created on demand when a tray is added. /// internal class DesignerFrame : Control, IOverlayService, ISplitWindowService, IContainsThemedScrollbarWindows @@ -65,8 +65,7 @@ internal Point AutoScrollPosition } /// - /// Demand creates a ptr to the BehaviorService - we do this so we can - /// route keyboard message to it. + /// Demand creates a ptr to the BehaviorService - we do this so we can route keyboard message to it. /// private BehaviorService BehaviorService { @@ -178,7 +177,7 @@ void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) } /// - /// We override this to do nothing. Otherwise, all the nice keyboard m messages we want would get run through the Form's keyboard handling procedure. + /// We override this to do nothing. Otherwise, all the nice keyboard messages we want would get run through the Form's keyboard handling procedure. /// protected override bool ProcessDialogKey(Keys keyData) { @@ -460,8 +459,7 @@ protected override void OnLayout(LayoutEventArgs e) private void ParentOverlay(Control control) { NativeMethods.SetParent(control.Handle, Handle); - SafeNativeMethods.SetWindowPos(control.Handle, (IntPtr)NativeMethods.HWND_TOP, 0, 0, 0, 0, - NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE); + SafeNativeMethods.SetWindowPos(control.Handle, (IntPtr)NativeMethods.HWND_TOP, 0, 0, 0, 0, NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE); } /// @@ -471,7 +469,7 @@ public int PushOverlay(Control control) { Debug.Assert(_overlayList.IndexOf(control) == -1, "Duplicate overlay in overlay service :" + control.GetType().FullName); _overlayList.Add(control); - // We cheat a bit here. We need to have these components parented, but we don't want them to effect our layout. + // We need to have these components parented, but we don't want them to effect our layout. if (IsHandleCreated) { ParentOverlay(control); @@ -528,8 +526,6 @@ public void InvalidateOverlays(Rectangle screenRectangle) /// public void InvalidateOverlays(Region screenRegion) { - - // paint in inverse order so that things at the front paint last. for (int i = _overlayList.Count - 1; i >= 0; i--) { @@ -537,20 +533,16 @@ public void InvalidateOverlays(Region screenRegion) { Rectangle overlayControlScreenBounds = overlayControl.Bounds; overlayControlScreenBounds.Location = overlayControl.PointToScreen(overlayControl.Location); - using (Region intersectionRegion = screenRegion.Clone()) { // get the intersection of everything on the screen that's invalidating and the overlaycontrol intersectionRegion.Intersect(overlayControlScreenBounds); - // translate this down to overlay control coordinates. intersectionRegion.Translate(-overlayControlScreenBounds.X, -overlayControlScreenBounds.Y); overlayControl.Invalidate(intersectionRegion); } - } } - } /// /// Need to know when child windows are created so we can properly set the Z-order @@ -558,7 +550,6 @@ public void InvalidateOverlays(Region screenRegion) protected override void WndProc(ref Message m) { base.WndProc(ref m); - if (m.Msg == NativeMethods.WM_PARENTNOTIFY && NativeMethods.Util.LOWORD(unchecked((int)(long)m.WParam)) == (short)NativeMethods.WM_CREATE) { if (_overlayList != null) @@ -577,8 +568,7 @@ protected override void WndProc(ref Message m) { foreach (Control c in _overlayList) { - SafeNativeMethods.SetWindowPos(c.Handle, (IntPtr)NativeMethods.HWND_TOP, 0, 0, 0, 0, - NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE); + SafeNativeMethods.SetWindowPos(c.Handle, (IntPtr)NativeMethods.HWND_TOP, 0, 0, 0, 0, NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE); } } } @@ -617,7 +607,6 @@ public override AccessibleObject HitTest(int x, int y) return cao; } } - return base.HitTest(x, y); } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionUIService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionUIService.cs index c67f85273f8..95157f84767 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionUIService.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionUIService.cs @@ -78,13 +78,6 @@ public SelectionUIService(IDesignerHost host) : base() OnTransactionOpened(host, EventArgs.Empty); } - IComponentChangeService cs = (IComponentChangeService)host.GetService(typeof(IComponentChangeService)); - if (cs != null) - { - cs.ComponentRemoved += new ComponentEventHandler(OnComponentRemove); - cs.ComponentChanged += new ComponentChangedEventHandler(OnComponentChanged); - } - // Listen to the SystemEvents so that we can resync selection based on display settings etc. SystemEvents.DisplaySettingsChanged += new EventHandler(OnSystemSettingChanged); SystemEvents.InstalledFontsChanged += new EventHandler(OnSystemSettingChanged); @@ -159,13 +152,6 @@ protected override void Dispose(bool disposing) { OnTransactionClosed(_host, new DesignerTransactionCloseEventArgs(true, true)); } - - IComponentChangeService cs = (IComponentChangeService)_host.GetService(typeof(IComponentChangeService)); - if (cs != null) - { - cs.ComponentRemoved -= new ComponentEventHandler(OnComponentRemove); - cs.ComponentChanged -= new ComponentChangedEventHandler(OnComponentChanged); - } } foreach (SelectionUIItem s in _selectionItems.Values) @@ -244,10 +230,7 @@ private HitTestInfo GetHitTest(Point value, int flags) return new HitTestInfo(SelectionUIItem.NOHIT, null); } - private ISelectionUIHandler GetHandler(object component) - { - return (ISelectionUIHandler)_selectionHandlers[component]; - } + private ISelectionUIHandler GetHandler(object component) => (ISelectionUIHandler)_selectionHandlers[component]; /// /// This method returns a well-formed name for a drag transaction based on the rules it is given. @@ -433,18 +416,12 @@ private void OnSelectionChanged(object sender, EventArgs e) /// /// User setting requires that we repaint. /// - private void OnSystemSettingChanged(object sender, EventArgs e) - { - Invalidate(); - } + private void OnSystemSettingChanged(object sender, EventArgs e) => Invalidate(); /// /// User setting requires that we repaint. /// - private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) - { - Invalidate(); - } + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) => Invalidate(); /// /// Inheriting classes should override this method to handle this event. Call super.onDragEnter to send this event to any registered event listeners. @@ -532,13 +509,13 @@ protected override void OnMouseDown(MouseEventArgs me) int hitTest = hti.hitTest; if ((hitTest & SelectionUIItem.CONTAINER_SELECTOR) != 0) { - _selSvc.SetSelectedComponents(new object[] { hti.selectionUIHit.component }, SelectionTypes.Auto); + _selSvc.SetSelectedComponents(new object[] { hti.selectionUIHit._component }, SelectionTypes.Auto); // Then do a drag... SelectionRules rules = SelectionRules.Moveable; if (((ISelectionUIService)this).BeginDrag(rules, anchor.X, anchor.Y)) { Visible = false; - _containerDrag = hti.selectionUIHit.component; + _containerDrag = hti.selectionUIHit._component; BeginMouseDrag(anchor, hitTest); } } @@ -549,7 +526,7 @@ protected override void OnMouseDown(MouseEventArgs me) _ctrlSelect = (Control.ModifierKeys & Keys.Control) != Keys.None; if (!_ctrlSelect) { - _selSvc.SetSelectedComponents(new object[] { hti.selectionUIHit.component }, SelectionTypes.Primary); + _selSvc.SetSelectedComponents(new object[] { hti.selectionUIHit._component }, SelectionTypes.Primary); } if ((hitTest & SelectionUIItem.MOVE_MASK) != 0) @@ -619,7 +596,7 @@ protected override void OnMouseMove(MouseEventArgs me) int hitTest = hti.hitTest; if (hitTest != SelectionUIItem.CONTAINER_SELECTOR && hti.selectionUIHit != null) { - OnContainerSelectorActive(new ContainerSelectorActiveEventArgs(hti.selectionUIHit.component)); + OnContainerSelectorActive(new ContainerSelectorActiveEventArgs(hti.selectionUIHit._component)); } if (_lastMoveScreenCoord == screenCoord) @@ -727,7 +704,7 @@ protected override void OnMouseUp(MouseEventArgs me) if (_ctrlSelect && !_mouseDragging && _selSvc != null) { HitTestInfo hti = GetHitTest(screenCoord, HITTEST_DEFAULT); - _selSvc.SetSelectedComponents(new object[] { hti.selectionUIHit.component }, SelectionTypes.Primary); + _selSvc.SetSelectedComponents(new object[] { hti.selectionUIHit._component }, SelectionTypes.Primary); } if (_mouseDragging) @@ -1071,8 +1048,7 @@ void ISelectionUIService.DragMoved(Rectangle offset) } Debug.Assert(_dragComponents != null, "We should have a set of drag controls here"); - if ((_dragRules & SelectionRules.Moveable) == SelectionRules.None - && (_dragRules & (SelectionRules.TopSizeable | SelectionRules.LeftSizeable)) == SelectionRules.None) + if ((_dragRules & SelectionRules.Moveable) == SelectionRules.None && (_dragRules & (SelectionRules.TopSizeable | SelectionRules.LeftSizeable)) == SelectionRules.None) { newOffset = new Rectangle(0, 0, offset.Width, offset.Height); } @@ -1161,7 +1137,7 @@ void ISelectionUIService.EndDrag(bool cancel) } /// - /// Filters the set of selected components. The selection service will retrieve all components that are currently selected. This method allows you to filter thisset down to components that match your criteria. The selectionRules parameter must contain one or more flags from the SelectionRules class. These flags allow you to constrain the set of selected objects to visible, movable, sizeable or all objects. + /// Filters the set of selected components. The selection service will retrieve all components that are currently selected. This method allows you to filter this set down to components that match your criteria. The selectionRules parameter must contain one or more flags from the SelectionRules class. These flags allow you to constrain the set of selected objects to visible, movable, sizeable or all objects. /// object[] ISelectionUIService.FilterSelection(object[] components, SelectionRules selectionRules) { @@ -1185,7 +1161,6 @@ object[] ISelectionUIService.FilterSelection(object[] components, SelectionRules } selection = (object[])list.ToArray(); } - return selection ?? (new object[0]); } @@ -1214,7 +1189,7 @@ Size ISelectionUIService.GetAdornmentDimensions(AdornmentType adornmentType) /// Determines if the component is currently "container" selected. Container selection is a visual aid for selecting containers. It doesn't affect the normal "component" selection. /// bool ISelectionUIService.GetContainerSelected(object component) => (component != null && _selectionItems[component] is ContainerSelectionUIItem); - + /// /// Retrieves a set of flags that define rules for the selection. Selection rules indicate if the given component can be moved or sized, for example. /// @@ -1382,32 +1357,32 @@ private class SelectionUIItem public const int GRABHANDLE_WIDTH = 7; public const int GRABHANDLE_HEIGHT = 7; // tables we use to determine how things can move and size - internal static readonly int[] activeSizeArray = new int[] { + internal static readonly int[] s_activeSizeArray = new int[] { SIZE_X | SIZE_Y | POS_LEFT | POS_TOP, SIZE_Y | POS_TOP, SIZE_X | SIZE_Y | POS_TOP | POS_RIGHT, SIZE_X | POS_LEFT, SIZE_X | POS_RIGHT, SIZE_X | SIZE_Y | POS_LEFT | POS_BOTTOM, SIZE_Y | POS_BOTTOM, SIZE_X | SIZE_Y | POS_RIGHT | POS_BOTTOM }; - internal static readonly Cursor[] activeCursorArrays = new Cursor[] { + internal static readonly Cursor[] s_activeCursorArrays = new Cursor[] { Cursors.SizeNWSE, Cursors.SizeNS, Cursors.SizeNESW, Cursors.SizeWE, Cursors.SizeWE, Cursors.SizeNESW, Cursors.SizeNS, Cursors.SizeNWSE }; - internal static readonly int[] inactiveSizeArray = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }; - internal static readonly Cursor[] inactiveCursorArray = new Cursor[] { + internal static readonly int[] s_inactiveSizeArray = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }; + internal static readonly Cursor[] s_inactiveCursorArray = new Cursor[] { Cursors.Arrow, Cursors.Arrow, Cursors.Arrow, Cursors.Arrow, Cursors.Arrow, Cursors.Arrow, Cursors.Arrow, Cursors.Arrow }; - internal int[] sizes; // array of sizing rules for this selection - internal Cursor[] cursors; // array of cursors for each grab location - internal SelectionUIService selUIsvc; - internal Rectangle innerRect = Rectangle.Empty; // inner part of selection (== control bounds) - internal Rectangle outerRect = Rectangle.Empty; // outer part of selection (inner + border size) - internal Region region; // region object that defines the shape - internal object component; // the component we're rendering + internal int[] _sizes; // array of sizing rules for this selection + internal Cursor[] _cursors; // array of cursors for each grab location + internal SelectionUIService _selUIsvc; + internal Rectangle _innerRect = Rectangle.Empty; // inner part of selection (== control bounds) + internal Rectangle _outerRect = Rectangle.Empty; // outer part of selection (inner + border size) + internal Region _region; // region object that defines the shape + internal object _component; // the component we're rendering private readonly Control _control; private SelectionStyles _selectionStyle; // how do we draw this thing? private SelectionRules _selectionRules; @@ -1417,13 +1392,13 @@ private class SelectionUIItem [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] public SelectionUIItem(SelectionUIService selUIsvc, object component) { - this.selUIsvc = selUIsvc; - this.component = component; + _selUIsvc = selUIsvc; + _component = component; _selectionStyle = SelectionStyles.Selected; // By default, a component isn't visible. We must establish what it can do through it's UI handler. _handler = selUIsvc.GetHandler(component); - sizes = inactiveSizeArray; - cursors = inactiveCursorArray; + _sizes = s_inactiveSizeArray; + _cursors = s_inactiveCursorArray; if (component is IComponent comp) { if (selUIsvc._host.GetDesigner(comp) is ControlDesigner cd) @@ -1447,10 +1422,10 @@ public virtual SelectionStyles Style if (value != _selectionStyle) { _selectionStyle = value; - if (region != null) + if (_region != null) { - region.Dispose(); - region = null; + _region.Dispose(); + _region = null; } } } @@ -1466,16 +1441,16 @@ public virtual void DoPaint(Graphics gr) if ((GetRules() & SelectionRules.Visible) == SelectionRules.None) return; bool fActive = false; - if (selUIsvc._selSvc != null) + if (_selUIsvc._selSvc != null) { - fActive = component == selUIsvc._selSvc.PrimarySelection; + fActive = _component == _selUIsvc._selSvc.PrimarySelection; // Office rules: If this is a multi-select, reverse the colors for active / inactive. - fActive = (fActive == (selUIsvc._selSvc.SelectionCount <= 1)); + fActive = (fActive == (_selUIsvc._selSvc.SelectionCount <= 1)); } - Rectangle r = new Rectangle(outerRect.X, outerRect.Y, GRABHANDLE_WIDTH, GRABHANDLE_HEIGHT); - Rectangle inner = innerRect; - Rectangle outer = outerRect; + Rectangle r = new Rectangle(_outerRect.X, _outerRect.Y, GRABHANDLE_WIDTH, GRABHANDLE_HEIGHT); + Rectangle inner = _innerRect; + Rectangle outer = _outerRect; Region oldClip = gr.Clip; Color borderColor = SystemColors.Control; if (_control != null && _control.Parent != null) @@ -1493,29 +1468,29 @@ public virtual void DoPaint(Graphics gr) if (((GetRules() & SelectionRules.Locked) == SelectionRules.None) && (GetRules() & SelectionRules.AllSizeable) != SelectionRules.None) { // upper left - ControlPaint.DrawGrabHandle(gr, r, fActive, (sizes[0] != 0)); + ControlPaint.DrawGrabHandle(gr, r, fActive, (_sizes[0] != 0)); // upper right r.X = inner.X + inner.Width; - ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[2] != 0); + ControlPaint.DrawGrabHandle(gr, r, fActive, _sizes[2] != 0); // lower right r.Y = inner.Y + inner.Height; - ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[7] != 0); + ControlPaint.DrawGrabHandle(gr, r, fActive, _sizes[7] != 0); // lower left r.X = outer.X; - ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[5] != 0); + ControlPaint.DrawGrabHandle(gr, r, fActive, _sizes[5] != 0); // lower middle r.X += (outer.Width - GRABHANDLE_WIDTH) / 2; - ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[6] != 0); + ControlPaint.DrawGrabHandle(gr, r, fActive, _sizes[6] != 0); // upper middle r.Y = outer.Y; - ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[1] != 0); + ControlPaint.DrawGrabHandle(gr, r, fActive, _sizes[1] != 0); // left middle r.X = outer.X; r.Y = inner.Y + (inner.Height - GRABHANDLE_HEIGHT) / 2; - ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[3] != 0); + ControlPaint.DrawGrabHandle(gr, r, fActive, _sizes[3] != 0); // right middle r.X = inner.X + inner.Width; - ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[4] != 0); + ControlPaint.DrawGrabHandle(gr, r, fActive, _sizes[4] != 0); } else { @@ -1550,7 +1525,7 @@ public virtual Cursor GetCursorAtPoint(Point pt) } else { - cursor = cursors[nOffset]; + cursor = _cursors[nOffset]; } } return cursor; @@ -1570,11 +1545,11 @@ public virtual int GetHitTest(Point pt) // Which index in the array is this? int nOffset = GetHandleIndexOfPoint(pt); // If no index, the user has picked on the hatch - if (-1 == nOffset || sizes[nOffset] == 0) + if (-1 == nOffset || _sizes[nOffset] == 0) { return ((GetRules() & SelectionRules.Moveable) == SelectionRules.None ? 0 : MOVE_X | MOVE_Y); } - return sizes[nOffset]; + return _sizes[nOffset]; } /// @@ -1582,51 +1557,51 @@ public virtual int GetHitTest(Point pt) /// private int GetHandleIndexOfPoint(Point pt) { - if (pt.X >= outerRect.X && pt.X <= innerRect.X) + if (pt.X >= _outerRect.X && pt.X <= _innerRect.X) { // Something on the left side. - if (pt.Y >= outerRect.Y && pt.Y <= innerRect.Y) + if (pt.Y >= _outerRect.Y && pt.Y <= _innerRect.Y) return 0; // top left - if (pt.Y >= innerRect.Y + innerRect.Height && pt.Y <= outerRect.Y + outerRect.Height) + if (pt.Y >= _innerRect.Y + _innerRect.Height && pt.Y <= _outerRect.Y + _outerRect.Height) return 5; // bottom left - if (pt.Y >= outerRect.Y + (outerRect.Height - GRABHANDLE_HEIGHT) / 2 - && pt.Y <= outerRect.Y + (outerRect.Height + GRABHANDLE_HEIGHT) / 2) + if (pt.Y >= _outerRect.Y + (_outerRect.Height - GRABHANDLE_HEIGHT) / 2 + && pt.Y <= _outerRect.Y + (_outerRect.Height + GRABHANDLE_HEIGHT) / 2) return 3; // middle left return -1; // unknown hit } - if (pt.Y >= outerRect.Y && pt.Y <= innerRect.Y) + if (pt.Y >= _outerRect.Y && pt.Y <= _innerRect.Y) { // something on the top - Debug.Assert(!(pt.X >= outerRect.X && pt.X <= innerRect.X), "Should be handled by left top check"); - if (pt.X >= innerRect.X + innerRect.Width && pt.X <= outerRect.X + outerRect.Width) + Debug.Assert(!(pt.X >= _outerRect.X && pt.X <= _innerRect.X), "Should be handled by left top check"); + if (pt.X >= _innerRect.X + _innerRect.Width && pt.X <= _outerRect.X + _outerRect.Width) return 2; // top right - if (pt.X >= outerRect.X + (outerRect.Width - GRABHANDLE_WIDTH) / 2 - && pt.X <= outerRect.X + (outerRect.Width + GRABHANDLE_WIDTH) / 2) + if (pt.X >= _outerRect.X + (_outerRect.Width - GRABHANDLE_WIDTH) / 2 + && pt.X <= _outerRect.X + (_outerRect.Width + GRABHANDLE_WIDTH) / 2) return 1; // top middle return -1; // unknown hit } - if (pt.X >= innerRect.X + innerRect.Width && pt.X <= outerRect.X + outerRect.Width) + if (pt.X >= _innerRect.X + _innerRect.Width && pt.X <= _outerRect.X + _outerRect.Width) { // something on the right side - Debug.Assert(!(pt.Y >= outerRect.Y && pt.Y <= innerRect.Y), "Should be handled by top right check"); - if (pt.Y >= innerRect.Y + innerRect.Height && pt.Y <= outerRect.Y + outerRect.Height) + Debug.Assert(!(pt.Y >= _outerRect.Y && pt.Y <= _innerRect.Y), "Should be handled by top right check"); + if (pt.Y >= _innerRect.Y + _innerRect.Height && pt.Y <= _outerRect.Y + _outerRect.Height) return 7; // bottom right - if (pt.Y >= outerRect.Y + (outerRect.Height - GRABHANDLE_HEIGHT) / 2 - && pt.Y <= outerRect.Y + (outerRect.Height + GRABHANDLE_HEIGHT) / 2) + if (pt.Y >= _outerRect.Y + (_outerRect.Height - GRABHANDLE_HEIGHT) / 2 + && pt.Y <= _outerRect.Y + (_outerRect.Height + GRABHANDLE_HEIGHT) / 2) return 4; // middle right return -1; // unknown hit } - if (pt.Y >= innerRect.Y + innerRect.Height && pt.Y <= outerRect.Y + outerRect.Height) + if (pt.Y >= _innerRect.Y + _innerRect.Height && pt.Y <= _outerRect.Y + _outerRect.Height) { // something on the bottom - Debug.Assert(!(pt.X >= outerRect.X && pt.X <= innerRect.X), "Should be handled by left bottom check"); + Debug.Assert(!(pt.X >= _outerRect.X && pt.X <= _innerRect.X), "Should be handled by left bottom check"); - Debug.Assert(!(pt.X >= innerRect.X + innerRect.Width && pt.X <= outerRect.X + outerRect.Width), "Should be handled by right bottom check"); + Debug.Assert(!(pt.X >= _innerRect.X + _innerRect.Width && pt.X <= _outerRect.X + _outerRect.Width), "Should be handled by right bottom check"); - if (pt.X >= outerRect.X + (outerRect.Width - GRABHANDLE_WIDTH) / 2 && pt.X <= outerRect.X + (outerRect.Width + GRABHANDLE_WIDTH) / 2) + if (pt.X >= _outerRect.X + (_outerRect.Width - GRABHANDLE_WIDTH) / 2 && pt.X <= _outerRect.X + (_outerRect.Width + GRABHANDLE_WIDTH) / 2) return 6; // bottom middle return -1; // unknown hit } @@ -1638,28 +1613,28 @@ private int GetHandleIndexOfPoint(Point pt) /// public virtual Region GetRegion() { - if (region == null) + if (_region == null) { - if ((GetRules() & SelectionRules.Visible) != SelectionRules.None && !outerRect.IsEmpty) + if ((GetRules() & SelectionRules.Visible) != SelectionRules.None && !_outerRect.IsEmpty) { - region = new Region(outerRect); - region.Exclude(innerRect); + _region = new Region(_outerRect); + _region.Exclude(_innerRect); } else { - region = new Region(new Rectangle(0, 0, 0, 0)); + _region = new Region(new Rectangle(0, 0, 0, 0)); } if (_handler != null) { - Rectangle handlerClip = _handler.GetSelectionClipRect(component); + Rectangle handlerClip = _handler.GetSelectionClipRect(_component); if (!handlerClip.IsEmpty) { - region.Intersect(selUIsvc.RectangleToClient(handlerClip)); + _region.Intersect(_selUIsvc.RectangleToClient(handlerClip)); } } } - return region; + return _region; } /// @@ -1669,10 +1644,10 @@ public virtual Region GetRegion() public void Dispose() { - if (region != null) + if (_region != null) { - region.Dispose(); - region = null; + _region.Dispose(); + _region = null; } } @@ -1681,9 +1656,9 @@ public void Dispose() /// public void Invalidate() { - if (!outerRect.IsEmpty && !selUIsvc.Disposing) + if (!_outerRect.IsEmpty && !_selUIsvc.Disposing) { - selUIsvc.Invalidate(outerRect); + _selUIsvc.Invalidate(_outerRect); } } @@ -1693,22 +1668,22 @@ public void Invalidate() protected bool PointWithinSelection(Point pt) { // This is only supported for visible selections - if ((GetRules() & SelectionRules.Visible) == SelectionRules.None || outerRect.IsEmpty || innerRect.IsEmpty) + if ((GetRules() & SelectionRules.Visible) == SelectionRules.None || _outerRect.IsEmpty || _innerRect.IsEmpty) { return false; } - if (pt.X < outerRect.X || pt.X > outerRect.X + outerRect.Width) + if (pt.X < _outerRect.X || pt.X > _outerRect.X + _outerRect.Width) { return false; } - if (pt.Y < outerRect.Y || pt.Y > outerRect.Y + outerRect.Height) + if (pt.Y < _outerRect.Y || pt.Y > _outerRect.Y + _outerRect.Height) { return false; } - if (pt.X > innerRect.X - && pt.X < innerRect.X + innerRect.Width - && pt.Y > innerRect.Y - && pt.Y < innerRect.Y + innerRect.Height) + if (pt.X > _innerRect.X + && pt.X < _innerRect.X + _innerRect.Width + && pt.Y > _innerRect.Y + && pt.Y < _innerRect.Y + _innerRect.Height) { return false; } @@ -1723,50 +1698,50 @@ private void UpdateGrabSettings() SelectionRules rules = GetRules(); if ((rules & SelectionRules.AllSizeable) == SelectionRules.None) { - sizes = inactiveSizeArray; - cursors = inactiveCursorArray; + _sizes = s_inactiveSizeArray; + _cursors = s_inactiveCursorArray; } else { - sizes = new int[8]; - cursors = new Cursor[8]; - Array.Copy(activeCursorArrays, cursors, cursors.Length); - Array.Copy(activeSizeArray, sizes, sizes.Length); + _sizes = new int[8]; + _cursors = new Cursor[8]; + Array.Copy(s_activeCursorArrays, _cursors, _cursors.Length); + Array.Copy(s_activeSizeArray, _sizes, _sizes.Length); if ((rules & SelectionRules.TopSizeable) != SelectionRules.TopSizeable) { - sizes[0] = 0; - sizes[1] = 0; - sizes[2] = 0; - cursors[0] = Cursors.Arrow; - cursors[1] = Cursors.Arrow; - cursors[2] = Cursors.Arrow; + _sizes[0] = 0; + _sizes[1] = 0; + _sizes[2] = 0; + _cursors[0] = Cursors.Arrow; + _cursors[1] = Cursors.Arrow; + _cursors[2] = Cursors.Arrow; } if ((rules & SelectionRules.LeftSizeable) != SelectionRules.LeftSizeable) { - sizes[0] = 0; - sizes[3] = 0; - sizes[5] = 0; - cursors[0] = Cursors.Arrow; - cursors[3] = Cursors.Arrow; - cursors[5] = Cursors.Arrow; + _sizes[0] = 0; + _sizes[3] = 0; + _sizes[5] = 0; + _cursors[0] = Cursors.Arrow; + _cursors[3] = Cursors.Arrow; + _cursors[5] = Cursors.Arrow; } if ((rules & SelectionRules.BottomSizeable) != SelectionRules.BottomSizeable) { - sizes[5] = 0; - sizes[6] = 0; - sizes[7] = 0; - cursors[5] = Cursors.Arrow; - cursors[6] = Cursors.Arrow; - cursors[7] = Cursors.Arrow; + _sizes[5] = 0; + _sizes[6] = 0; + _sizes[7] = 0; + _cursors[5] = Cursors.Arrow; + _cursors[6] = Cursors.Arrow; + _cursors[7] = Cursors.Arrow; } if ((rules & SelectionRules.RightSizeable) != SelectionRules.RightSizeable) { - sizes[2] = 0; - sizes[4] = 0; - sizes[7] = 0; - cursors[2] = Cursors.Arrow; - cursors[4] = Cursors.Arrow; - cursors[7] = Cursors.Arrow; + _sizes[2] = 0; + _sizes[4] = 0; + _sizes[7] = 0; + _cursors[2] = Cursors.Arrow; + _cursors[4] = Cursors.Arrow; + _cursors[7] = Cursors.Arrow; } } } @@ -1783,7 +1758,7 @@ public void UpdateRules() else { SelectionRules oldRules = _selectionRules; - _selectionRules = _handler.GetComponentRules(component); + _selectionRules = _handler.GetComponentRules(_component); if (_selectionRules != oldRules) { UpdateGrabSettings(); @@ -1803,21 +1778,21 @@ public virtual bool UpdateSize() return false; if ((GetRules() & SelectionRules.Visible) == SelectionRules.None) return false; - innerRect = _handler.GetComponentBounds(component); - if (!innerRect.IsEmpty) + _innerRect = _handler.GetComponentBounds(_component); + if (!_innerRect.IsEmpty) { - innerRect = selUIsvc.RectangleToClient(innerRect); - Rectangle rcOuterNew = new Rectangle( innerRect.X - GRABHANDLE_WIDTH, innerRect.Y - GRABHANDLE_HEIGHT, innerRect.Width + 2 * GRABHANDLE_WIDTH, innerRect.Height + 2 * GRABHANDLE_HEIGHT); - if (outerRect.IsEmpty || !outerRect.Equals(rcOuterNew)) + _innerRect = _selUIsvc.RectangleToClient(_innerRect); + Rectangle rcOuterNew = new Rectangle( _innerRect.X - GRABHANDLE_WIDTH, _innerRect.Y - GRABHANDLE_HEIGHT, _innerRect.Width + 2 * GRABHANDLE_WIDTH, _innerRect.Height + 2 * GRABHANDLE_HEIGHT); + if (_outerRect.IsEmpty || !_outerRect.Equals(rcOuterNew)) { - if (!outerRect.IsEmpty) + if (!_outerRect.IsEmpty) Invalidate(); - outerRect = rcOuterNew; + _outerRect = rcOuterNew; Invalidate(); - if (region != null) + if (_region != null) { - region.Dispose(); - region = null; + _region.Dispose(); + _region = null; } sizeChanged = true; } @@ -1825,8 +1800,8 @@ public virtual bool UpdateSize() else { Rectangle rcNew = new Rectangle(0, 0, 0, 0); - sizeChanged = outerRect.IsEmpty || !outerRect.Equals(rcNew); - innerRect = outerRect = rcNew; + sizeChanged = _outerRect.IsEmpty || !_outerRect.Equals(rcNew); + _innerRect = _outerRect = rcNew; } return sizeChanged; } @@ -1856,9 +1831,9 @@ public override Cursor GetCursorAtPoint(Point pt) public override int GetHitTest(Point pt) { int ht = NOHIT; - if ((GetRules() & SelectionRules.Visible) != SelectionRules.None && !outerRect.IsEmpty) + if ((GetRules() & SelectionRules.Visible) != SelectionRules.None && !_outerRect.IsEmpty) { - Rectangle r = new Rectangle(outerRect.X, outerRect.Y, CONTAINER_WIDTH, CONTAINER_HEIGHT); + Rectangle r = new Rectangle(_outerRect.X, _outerRect.Y, CONTAINER_WIDTH, CONTAINER_HEIGHT); if (r.Contains(pt)) { @@ -1877,25 +1852,25 @@ public override void DoPaint(Graphics gr) // If we're not visible, then there's nothing to do... if ((GetRules() & SelectionRules.Visible) == SelectionRules.None) return; - Rectangle glyphBounds = new Rectangle(outerRect.X, outerRect.Y, CONTAINER_WIDTH, CONTAINER_HEIGHT); + Rectangle glyphBounds = new Rectangle(_outerRect.X, _outerRect.Y, CONTAINER_WIDTH, CONTAINER_HEIGHT); ControlPaint.DrawContainerGrabHandle(gr, glyphBounds); } public override Region GetRegion() { - if (region == null) + if (_region == null) { - if ((GetRules() & SelectionRules.Visible) != SelectionRules.None && !outerRect.IsEmpty) + if ((GetRules() & SelectionRules.Visible) != SelectionRules.None && !_outerRect.IsEmpty) { - Rectangle r = new Rectangle(outerRect.X, outerRect.Y, CONTAINER_WIDTH, CONTAINER_HEIGHT); - region = new Region(r); + Rectangle r = new Rectangle(_outerRect.X, _outerRect.Y, CONTAINER_WIDTH, CONTAINER_HEIGHT); + _region = new Region(r); } else { - region = new Region(new Rectangle(0, 0, 0, 0)); + _region = new Region(new Rectangle(0, 0, 0, 0)); } } - return region; + return _region; } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemBehavior.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemBehavior.cs index 2c0f1251d00..fce793214cc 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemBehavior.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemBehavior.cs @@ -275,7 +275,7 @@ public override bool OnMouseDown(Glyph g, MouseButtons button, Point mouseLoc) { if (keyService.ActiveTemplateNode.IsSystemContextMenuDisplayed) { - // DevDiv Bugs: 144618 : skip behaviors when the context menu is displayed + // skip behaviors when the context menu is displayed return false; } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs index 253557fa872..61f6321639e 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs @@ -524,7 +524,6 @@ private void InsertIntoStatusStrip(StatusStrip parent, Type t) /// /// Insert Item into ToolStrip. /// - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] private void InsertToolStripItem(Type t) diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs index f8694fcab1e..95e1af5e023 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs @@ -184,7 +184,6 @@ internal object OwnerItemAfterCut set => _ownerItemAfterCut = value; } - // When shift key is pressed we need to know where to start from .. this object keeps a track of that item. internal object ShiftPrimaryItem { @@ -1835,7 +1834,7 @@ public void RotateTab(bool backwards) } } } - // ctl is NOT A CONTROL ... so its Component. Try this for WinBarItem. + // ctl is NOT A CONTROL ... so its Component. Try this for ToolStripItem. if (targetSelection == null && ctl == null) { ToolStripItem item = selSvc.PrimarySelection as ToolStripItem; diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs index 90e6275bcd8..85ec14fa01d 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs @@ -304,7 +304,6 @@ private ToolStripKeyboardHandlingService KeyboardHandlingService } } - /// /// ParentComponent in case of MenuItems on the DropDown is the OwnerItem of the DropDown and not the ToolStripDropDown (since the DropDowns are not sited) /// @@ -988,16 +987,6 @@ protected override void Dispose(bool disposing) toolStripAdornerWindowService = null; } - // unhook notifications. - IComponentChangeService componentChangeSvc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); - if (componentChangeSvc != null) - { - componentChangeSvc.ComponentRemoved -= new ComponentEventHandler(ComponentChangeSvc_ComponentRemoved); - componentChangeSvc.ComponentRemoving -= new ComponentEventHandler(ComponentChangeSvc_ComponentRemoving); - componentChangeSvc.ComponentAdding -= new ComponentEventHandler(ComponentChangeSvc_ComponentAdding); - componentChangeSvc.ComponentAdded -= new ComponentEventHandler(ComponentChangeSvc_ComponentAdded); - } - if (typeHereTemplateNode != null) { typeHereTemplateNode.RollBack(); @@ -1516,16 +1505,6 @@ public override void Initialize(IComponent component) //Set the DoubleClickEnabled MenuItem.DoubleClickEnabled = true; - // attach notifcations. - IComponentChangeService componentChangeSvc = (IComponentChangeService)GetService(typeof(IComponentChangeService)); - if (componentChangeSvc != null) - { - componentChangeSvc.ComponentRemoved += new ComponentEventHandler(ComponentChangeSvc_ComponentRemoved); - componentChangeSvc.ComponentRemoving += new ComponentEventHandler(ComponentChangeSvc_ComponentRemoving); - componentChangeSvc.ComponentAdding += new ComponentEventHandler(ComponentChangeSvc_ComponentAdding); - componentChangeSvc.ComponentAdded += new ComponentEventHandler(ComponentChangeSvc_ComponentAdded); - } - if (undoEngine == null) { undoEngine = GetService(typeof(UndoEngine)) as UndoEngine; @@ -1836,6 +1815,7 @@ private void CommitInsertTransaction(bool commit) } } } + /// /// Checks if the component being added is a child ToolStripItem. /// diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs index c3843f0f75d..b16700b07ed 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs @@ -138,36 +138,8 @@ public ToolStripTemplateNode(IComponent component, string text, Image image) } SetupNewEditNode(this, text, image, component); - _commands = new MenuCommand[] { - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyMoveUp), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyMoveDown), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyMoveLeft), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyMoveRight), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.Delete), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.Cut), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.Copy), - - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeUp), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeDown), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeLeft), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeRight), - - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeySizeWidthIncrease), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeySizeHeightIncrease), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeySizeWidthDecrease), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeySizeHeightDecrease), - - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeWidthIncrease), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeHeightIncrease), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeWidthDecrease), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.KeyNudgeHeightDecrease) - - }; - - _addCommands = new MenuCommand[] { - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.Undo), - new MenuCommand(new EventHandler(OnMenuCut), MenuCommands.Redo) - }; + _commands = new MenuCommand[] { }; + _addCommands = new MenuCommand[] { }; } /// @@ -228,7 +200,6 @@ public bool Active } else { - //Active == false.. Fire Deactivated OnDeactivated(new EventArgs()); if (KeyboardService != null) { From 589dd7db7fedc7de8bb672755ef5178648b242b7 Mon Sep 17 00:00:00 2001 From: Judit Varsanyi Rozsa Date: Fri, 5 Apr 2019 18:20:02 -0700 Subject: [PATCH 05/10] PR comments, replace winbar to ToolStrip in all files --- .../Windows/Forms/Design/DesignerActionUI.cs | 11 +- .../Windows/Forms/Design/DesignerFrame.cs | 5 - .../Design/DesignerToolStripControlHost.cs | 2 +- .../Windows/Forms/Design/DesignerUtils.cs | 9 +- .../Forms/Design/FormDocumentDesigner.cs | 27 ----- .../Forms/Design/NewItemsContextMenuStrip.cs | 2 +- .../Forms/Design/ParentControlDesigner.cs | 6 - .../Windows/Forms/Design/SelectionManager.cs | 29 +---- .../Design/ToolStripAdornerWindowService.cs | 2 +- .../Windows/Forms/Design/ToolStripDesigner.cs | 112 +++--------------- .../Forms/Design/ToolStripDropDownDesigner.cs | 2 +- .../Forms/Design/ToolStripItemDesigner.cs | 2 +- .../ToolStripKeyboardHandlingService.cs | 2 +- .../Forms/Design/ToolStripMenuItemDesigner.cs | 2 +- .../Forms/Design/ToolStripTemplateNode.cs | 4 +- .../Windows/Forms/ImageIndexConverter.cs | 4 +- .../System/Windows/Forms/ImageKeyConverter.cs | 4 +- .../System/Windows/Forms/SplitContainer.cs | 4 +- .../src/System/Windows/Forms/ToolStrip.cs | 34 +++--- .../System/Windows/Forms/ToolStripComboBox.cs | 78 ++++++------ .../Windows/Forms/ToolStripControlHost.cs | 12 +- .../System/Windows/Forms/ToolStripDropDown.cs | 14 +-- .../Windows/Forms/ToolStripDropDownButton.cs | 2 +- .../Windows/Forms/ToolStripDropDownItem.cs | 4 +- .../Windows/Forms/ToolStripDropDownMenu.cs | 4 +- .../Forms/ToolStripDropTargetManager.cs | 4 +- .../Forms/ToolStripGripRenderEventHandler.cs | 2 +- .../src/System/Windows/Forms/ToolStripItem.cs | 18 +-- .../Windows/Forms/ToolStripItemEventType.cs | 2 +- .../ToolStripItemImageRenderEventArgs.cs | 2 +- .../ToolStripItemImageRenderEventHandler.cs | 2 +- .../Windows/Forms/ToolStripItemOverflow.cs | 2 +- .../Windows/Forms/ToolStripItemPlacement.cs | 2 +- .../Forms/ToolStripItemRenderEventArgs.cs | 2 +- .../Forms/ToolStripItemRenderEventHandler.cs | 2 +- .../Forms/ToolStripItemTextRenderEventArgs.cs | 6 +- .../ToolStripItemTextRenderEventHandler.cs | 2 +- .../System/Windows/Forms/ToolStripLabel.cs | 4 +- .../System/Windows/Forms/ToolStripMenuItem.cs | 16 +-- .../System/Windows/Forms/ToolStripOverflow.cs | 2 +- .../Windows/Forms/ToolStripOverflowButton.cs | 4 +- .../System/Windows/Forms/ToolStripPanelRow.cs | 2 +- .../Forms/ToolStripRenderEventHandler.cs | 2 +- .../System/Windows/Forms/ToolStripRenderer.cs | 22 ++-- .../Windows/Forms/ToolStripScrollButton.cs | 2 +- .../Windows/Forms/ToolStripSeparator.cs | 22 ++-- .../ToolStripSeparatorRenderEventArgs.cs | 4 +- .../ToolStripSeparatorRenderEventHandler.cs | 2 +- .../Windows/Forms/ToolStripSplitButton.cs | 4 +- .../Forms/ToolStripSplitStackLayout.cs | 8 +- .../Windows/Forms/ToolStripStatusLabel.cs | 4 +- .../Windows/Forms/ToolStripSystemRenderer.cs | 14 +-- .../System/Windows/Forms/ToolStripTextBox.cs | 104 ++++++++-------- 53 files changed, 243 insertions(+), 396 deletions(-) diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUI.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUI.cs index 27dc6afc343..90605451d11 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUI.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUI.cs @@ -312,7 +312,6 @@ private void DesignerTransactionClosed(object sender, DesignerTransactionCloseEv private void RecreateInternal(IComponent comp) { - //Debug.WriteLine("not in a transaction, do it now!"); DesignerActionGlyph glyph = GetDesignerActionGlyph(comp); if (glyph != null) { @@ -701,17 +700,13 @@ internal void ShowDesignerActionPanel(IComponent relatedComponent, DesignerActio designerActionHost.AccessibleName = string.Format(SR.DesignerActionPanel_DefaultPanelTitle, relatedComponent.GetType().Name); panel.AccessibleName = string.Format(SR.DesignerActionPanel_DefaultPanelTitle, relatedComponent.GetType().Name); - - //GetDesignerActionGlyph(relatedComponent); // only here to update the ActionList collection on the behavior designerActionHost.SetDesignerActionPanel(panel, glyph); Point location = UpdateDAPLocation(relatedComponent, glyph); // check that the panel will have at least it's parent glyph visible on the adorner window - if (_behaviorService != null && _behaviorService.AdornerWindowControl.DisplayRectangle.IntersectsWith(glyph.Bounds)) - { - //behaviorService.AdornerWindowGraphics.IsVisible(glyph.Bounds)) { + { if (_mainParentWindow != null && _mainParentWindow.Handle != IntPtr.Zero) { Debug.WriteLineIf(s_designeActionPanelTraceSwitch.TraceVerbose, "Assigning owner to mainParentWindow"); @@ -799,7 +794,7 @@ public void UpdateContainerSize() } public void CheckFocusIsRight() - { // hack to get the focus to NOT stay on ContainerControl + { // fix to get the focus to NOT stay on ContainerControl Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "Checking focus..."); IntPtr focusedControl = UnsafeNativeMethods.GetFocus(); if (focusedControl == Handle) @@ -1022,7 +1017,7 @@ internal static string GetControlInformation(IntPtr hwnd) } return sb.ToString() + "\r\n\t\t\tType: [" + typeOfControl + "] Name: [" + nameOfControl + "]"; #else - return String.Empty; + return string.Empty; #endif } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerFrame.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerFrame.cs index c150dca91cc..4150f9dd24d 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerFrame.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerFrame.cs @@ -89,7 +89,6 @@ protected override void Dispose(bool disposing) _designer = null; designerHolder.Visible = false; designerHolder.Parent = null; - SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); } if (_splitter != null) { @@ -122,10 +121,6 @@ public void Initialize(Control view) SyncDesignerUI(); _designer.Visible = true; _designer.Enabled = true; - // We need to force handle creation here, since setting Visible = true won't if the control is already Visible = true. (UserControl starts out Visible true, Form does not) This guarantees that as controls are added to the root component their handles will be created correctly, and not the first time they're queried after load. - IntPtr handle = _designer.Handle; - // Hook the handler here, when we know that the designer object has already been set - SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); } /// diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerToolStripControlHost.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerToolStripControlHost.cs index 87a141bf197..8e5edf60a6d 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerToolStripControlHost.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerToolStripControlHost.cs @@ -9,7 +9,7 @@ namespace System.Windows.Forms.Design { /// - /// This internal class is used by the new ToolStripDesigner to add a dummy node to the end. This class inherits from WinBarControlHost and overrides the CanSelect property so that the dummy Node when shown in the designer doesnt show selection on Mouse movements. The image is set to theDummyNodeImage embedded into the resources. + /// This internal class is used by the new ToolStripDesigner to add a dummy node to the end. This class inherits from ToolStripControlHost and overrides the CanSelect property so that the dummy Node when shown in the designer doesnt show selection on Mouse movements. The image is set to theDummyNodeImage embedded into the resources. /// internal class DesignerToolStripControlHost : ToolStripControlHost, IComponent diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerUtils.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerUtils.cs index 2cde053640b..e4b32a96eeb 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerUtils.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerUtils.cs @@ -120,11 +120,11 @@ static DesignerUtils() HANDLESIZE = DpiHelper.LogicalToDeviceUnitsX(HANDLESIZE); HANDLEOVERLAP = DpiHelper.LogicalToDeviceUnitsX(HANDLEOVERLAP); NORESIZEHANDLESIZE = DpiHelper.LogicalToDeviceUnitsX(NORESIZEHANDLESIZE); - LOCKHANDLEHEIGHT = DpiHelper.LogicalToDeviceUnitsY(LOCKHANDLEHEIGHT); + LOCKHANDLEHEIGHT = DpiHelper.LogicalToDeviceUnitsX(LOCKHANDLEHEIGHT); LOCKHANDLEWIDTH = DpiHelper.LogicalToDeviceUnitsX(LOCKHANDLEWIDTH); LOCKHANDLEOVERLAP = DpiHelper.LogicalToDeviceUnitsX(LOCKHANDLEOVERLAP); LOCKHANDLESIZE_UPPER = DpiHelper.LogicalToDeviceUnitsX(LOCKHANDLESIZE_UPPER); - LOCKHANDLEHEIGHT_LOWER = DpiHelper.LogicalToDeviceUnitsY(LOCKHANDLEHEIGHT_LOWER); + LOCKHANDLEHEIGHT_LOWER = DpiHelper.LogicalToDeviceUnitsX(LOCKHANDLEHEIGHT_LOWER); LOCKHANDLEWIDTH_LOWER = DpiHelper.LogicalToDeviceUnitsX(LOCKHANDLEWIDTH_LOWER); CONTAINERGRABHANDLESIZE = DpiHelper.LogicalToDeviceUnitsX(CONTAINERGRABHANDLESIZE); RESIZEGLYPHSIZE = DpiHelper.LogicalToDeviceUnitsX(RESIZEGLYPHSIZE); @@ -854,7 +854,6 @@ public static ICollection CopyDragObjects(ICollection objects, IServiceProvider } else if (c == null) { // this happens when we are dragging a toolstripitem - // TODO: Can we remove the ToolStrip specific code? if (comp is ToolStripItem item && item.GetCurrentParent() == null) { newObjects.Add(comp); @@ -929,7 +928,7 @@ public static void ApplyTreeViewThemeStyles(TreeView treeView) { if (treeView == null) { - throw new ArgumentNullException("treeView"); + throw new ArgumentNullException(nameof(treeView)); } treeView.HotTracking = true; treeView.ShowLines = false; @@ -953,7 +952,7 @@ public static void ApplyListViewThemeStyles(ListView listView) { if (listView == null) { - throw new ArgumentNullException("listView"); + throw new ArgumentNullException(nameof(listView)); } IntPtr hwnd = listView.Handle; SafeNativeMethods.SetWindowTheme(hwnd, "Explorer", null); diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs index d307af9671b..5d40d301550 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs @@ -346,19 +346,6 @@ protected override void Dispose(bool disposing) { IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); Debug.Assert(host != null, "Must have a designer host on dispose"); - if (host != null) - { - host.LoadComplete -= new EventHandler(OnLoadComplete); - host.Activated -= new EventHandler(OnDesignerActivate); - host.Deactivated -= new EventHandler(OnDesignerDeactivate); - } - - IComponentChangeService cs = (IComponentChangeService)GetService(typeof(IComponentChangeService)); - if (cs != null) - { - cs.ComponentAdded -= new ComponentEventHandler(OnComponentAdded); - cs.ComponentRemoved -= new ComponentEventHandler(OnComponentRemoved); - } } base.Dispose(disposing); } @@ -474,25 +461,11 @@ public override void Initialize(IComponent component) _initializing = false; AutoResizeHandles = true; Debug.Assert(component is Form, "FormDocumentDesigner expects its component to be a form."); - IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); - if (host != null) - { - host.LoadComplete += new EventHandler(OnLoadComplete); - host.Activated += new EventHandler(OnDesignerActivate); - host.Deactivated += new EventHandler(OnDesignerDeactivate); - } Form form = (Form)Control; form.WindowState = FormWindowState.Normal; ShadowProperties["AcceptButton"] = form.AcceptButton; ShadowProperties["CancelButton"] = form.CancelButton; - // Monitor component/remove add events for our tray - IComponentChangeService cs = (IComponentChangeService)GetService(typeof(IComponentChangeService)); - if (cs != null) - { - cs.ComponentAdded += new ComponentEventHandler(OnComponentAdded); - cs.ComponentRemoved += new ComponentEventHandler(OnComponentRemoved); - } } /// diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/NewItemsContextMenuStrip.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/NewItemsContextMenuStrip.cs index 7d0d5b4a437..7a16472c30d 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/NewItemsContextMenuStrip.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/NewItemsContextMenuStrip.cs @@ -59,7 +59,7 @@ protected override void OnOpening(CancelEventArgs e) base.OnOpening(e); } - // Please refer to VsW: 505199 for more details. We dont want the runtime behavior for this Design Time only DropDown and hence we overide the ProcessDialogKey and just close the DropDown instead of running through the runtime implementation for RIGHT/LEFT Keys which ends up setting ModalMenuFilter. + // We dont want the runtime behavior for this Design Time only DropDown and hence we overide the ProcessDialogKey and just close the DropDown instead of running through the runtime implementation for RIGHT/LEFT Keys which ends up setting ModalMenuFilter. protected override bool ProcessDialogKey(Keys keyData) { Keys keyCode = (Keys)keyData & Keys.KeyCode; diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ParentControlDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ParentControlDesigner.cs index 3e1bd27a84b..55c676db641 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ParentControlDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ParentControlDesigner.cs @@ -533,16 +533,10 @@ public override void Initialize(IComponent component) ((ScrollableControl)Control).Scroll += new ScrollEventHandler(OnScroll); } EnableDragDrop(true); - // Hook load events. At the end of load, we need to do a scan through all of our child controls to see which ones are being inherited. We connect these up. IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); if (host != null) { componentChangeSvc = (IComponentChangeService)host.GetService(typeof(IComponentChangeService)); - if (componentChangeSvc != null) - { - componentChangeSvc.ComponentRemoving += new ComponentEventHandler(OnComponentRemoving); - componentChangeSvc.ComponentRemoved += new ComponentEventHandler(OnComponentRemoved); - } } // update the Status Command statusCommandUI = new StatusCommandUI(component.Site); diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionManager.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionManager.cs index 76d211816a2..3b665359cbf 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionManager.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionManager.cs @@ -12,7 +12,8 @@ namespace System.Windows.Forms.Design { /// - /// The SelectionBehavior is pushed onto the BehaviorStack in response to apositively hit tested SelectionGlyph. The SelectionBehavior performs two main tasks: 1) forward messages to the related ControlDesigner, and 2) calls upon the SelectionManager to push a potention DragBehavior. + /// The SelectionBehavior is pushed onto the BehaviorStack in response to apositively hit tested SelectionGlyph. The SelectionBehavior performs two main tasks: 1) forward messages to the related ControlDesigner, and 2) calls upon the SelectionManager to push a potention DragBehavior. + /// internal sealed class SelectionManager : IDisposable { private Adorner _selectionAdorner;//used to provide all selection glyphs @@ -47,28 +48,13 @@ public SelectionManager(IServiceProvider serviceProvider, BehaviorService behavi Debug.Fail("SelectionManager - Host or SelSvc is null, can't continue"); } - //sync the BehaviorService's begindrag event - behaviorService.BeginDrag += new BehaviorDragDropEventHandler(OnBeginDrag); - //sync the BehaviorService's Synchronize event - behaviorService.Synchronize += new EventHandler(OnSynchronize); - _selSvc.SelectionChanged += new EventHandler(OnSelectionChanged); _rootComponent = (Control)_designerHost.RootComponent; - //create and add both of our adorners, one for selection, one for bodies _selectionAdorner = new Adorner(); _bodyAdorner = new Adorner(); behaviorService.Adorners.Add(_bodyAdorner); behaviorService.Adorners.Add(_selectionAdorner);//adding this will cause the adorner to get setup with a ptr to the beh.svc. _componentToDesigner = new Hashtable(); - IComponentChangeService cs = (IComponentChangeService)serviceProvider.GetService(typeof(IComponentChangeService)); - if (cs != null) - { - cs.ComponentAdded += new ComponentEventHandler(OnComponentAdded); - cs.ComponentRemoved += new ComponentEventHandler(OnComponentRemoved); - cs.ComponentChanged += new ComponentChangedEventHandler(OnComponentChanged); - } - - _designerHost.TransactionClosed += new DesignerTransactionCloseEventHandler(OnTransactionClosed); // designeraction UI if (_designerHost.GetService(typeof(DesignerOptionService)) is DesignerOptionService options) { @@ -188,21 +174,12 @@ public void Dispose() { if (_designerHost != null) { - _designerHost.TransactionClosed -= new DesignerTransactionCloseEventHandler(OnTransactionClosed); _designerHost = null; } if (_serviceProvider != null) { - IComponentChangeService cs = (IComponentChangeService)_serviceProvider.GetService(typeof(IComponentChangeService)); - if (cs != null) - { - cs.ComponentAdded -= new ComponentEventHandler(OnComponentAdded); - cs.ComponentChanged -= new ComponentChangedEventHandler(OnComponentChanged); - cs.ComponentRemoved -= new ComponentEventHandler(OnComponentRemoved); - } if (_selSvc != null) { - _selSvc.SelectionChanged -= new EventHandler(OnSelectionChanged); _selSvc = null; } _serviceProvider = null; @@ -211,8 +188,6 @@ public void Dispose() { _behaviorService.Adorners.Remove(_bodyAdorner); _behaviorService.Adorners.Remove(_selectionAdorner); - _behaviorService.BeginDrag -= new BehaviorDragDropEventHandler(OnBeginDrag); - _behaviorService.Synchronize -= new EventHandler(OnSynchronize); _behaviorService = null; } if (_selectionAdorner != null) diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripAdornerWindowService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripAdornerWindowService.cs index 08a9a983b3e..01ff504fd46 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripAdornerWindowService.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripAdornerWindowService.cs @@ -202,7 +202,7 @@ protected override CreateParams CreateParams { CreateParams cp = base.CreateParams; cp.Style &= ~(NativeMethods.WS_CLIPCHILDREN | NativeMethods.WS_CLIPSIBLINGS); - cp.ExStyle |= 0x00000020/*WS_EX_TRANSPARENT*/; + cp.ExStyle |= NativeMethods.WS_EX_TRANSPARENT; return cp; } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesigner.cs index ae0cba3275a..335ad55c2a3 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesigner.cs @@ -32,20 +32,19 @@ internal class ToolStripDesigner : ControlDesigner private ToolStripEditorManager _editManager = null; // newly added editor manager ... private ToolStrip _miniToolStrip = null;// the toolStrip that hosts the "New Template Node" button private DesignerTransaction _insertMenuItemTransaction = null; //There Should be one and only one Pending insertTransaction. - private Rectangle _dragBoxFromMouseDown = Rectangle.Empty; //Needed to Store the DRAGDROP Rect from the WinbarItemBehavior. + private Rectangle _dragBoxFromMouseDown = Rectangle.Empty; //Needed to Store the DRAGDROP Rect from the ToolStripItemBehavior. private int _indexOfItemUnderMouseToDrag = -1; //defaulted to invalid index andwill be set by the behaviour. private ToolStripTemplateNode _tn = null; //templateNode private ISelectionService _selectionSvc = null; // cached selection service. - private uint _editingCollection = 0; // non-zero if the collection editor is up for this winbar or a child of it. + private uint _editingCollection = 0; // non-zero if the collection editor is up for this ToolStrip or a child of it. private DesignerTransaction _pendingTransaction = null; // our transaction for adding/removing items. - private bool _addingItem = false; // true if we are expecting to be notified of adding a WinbarItem to the designer. + private bool _addingItem = false; // true if we are expecting to be notified of adding a ToolStripItem to the designer. private Rectangle _boundsToInvalidate = Rectangle.Empty; //Bounds to Invalidate if a DropDownItem is Deleted private bool _currentVisible = true; // Change Visibility private ToolStripActionList _actionLists; // Action List on Chrome... private ToolStripAdornerWindowService _toolStripAdornerWindowService = null; // Add the Adorner Service for OverFlow DropDown... private IDesignerHost _host = null;//get private copy of the DesignerHost private IComponentChangeService _componentChangeSvc; - private UndoEngine _undoEngine = null; private bool _undoingCalled = false; private IToolboxService _toolboxService; private ContextMenuStrip _toolStripContextMenu; @@ -233,7 +232,7 @@ public Rectangle DragBoxFromMouseDown } /// - /// Set by the ToolStripItemCollectionEditor when it's launched for this winbar so we won't pick up it's items when added. We count this so that we can deal with nestings. + /// Set by the ToolStripItemCollectionEditor when it's launched for this ToolStrip so we won't pick up it's items when added. We count this so that we can deal with nestings. /// internal bool EditingCollection { @@ -469,7 +468,7 @@ internal bool Visible /// private void AddBodyGlyphsForOverflow() { - // now walk the winbar and add glyphs for each of it's children + // now walk the ToolStrip and add glyphs for each of it's children foreach (ToolStripItem item in ToolStrip.Items) { if (item is DesignerToolStripControlHost) @@ -897,22 +896,11 @@ internal bool Commit() /// private void Control_HandleCreated(object sender, EventArgs e) { - Control.HandleCreated -= new EventHandler(Control_HandleCreated); InitializeNewItemDropDown(); - // we should HOOK this event here since getting the OverFlowButton property causes handle creation which we need to avoid hook the designer for the OverFlow Item events.. - ToolStrip.OverflowButton.DropDown.Closing += new ToolStripDropDownClosingEventHandler(OnOverflowDropDownClosing); - ToolStrip.OverflowButton.DropDownOpening += new EventHandler(OnOverFlowDropDownOpening); - ToolStrip.OverflowButton.DropDownOpened += new EventHandler(OnOverFlowDropDownOpened); - ToolStrip.OverflowButton.DropDownClosed += new EventHandler(OnOverFlowDropDownClosed); - ToolStrip.OverflowButton.DropDown.Resize += new System.EventHandler(OnOverflowDropDownResize); - ToolStrip.OverflowButton.DropDown.Paint += new System.Windows.Forms.PaintEventHandler(OnOverFlowDropDownPaint); - ToolStrip.Move += new System.EventHandler(OnToolStripMove); - ToolStrip.VisibleChanged += new System.EventHandler(OnToolStripVisibleChanged); - ToolStrip.ItemAdded += new ToolStripItemEventHandler(OnItemAdded); } /// - /// Fired after a component has been added. Here, we add it to the winbar and select it. + /// Fired after a component has been added. Here, we add it to the ToolStrip and select it. /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] private void ComponentChangeSvc_ComponentAdded(object sender, ComponentEventArgs e) @@ -1011,7 +999,7 @@ private void ComponentChangeSvc_ComponentAdding(object sender, ComponentEventArg return; } - // we'll be adding a child item if the component is a winbar item and we've currently got this winbar or one of it's items selected. we do this so things like paste and undo automagically work. + // we'll be adding a child item if the component is a ToolStrip item and we've currently got this ToolStrip or one of it's items selected. we do this so things like paste and undo automagically work. ToolStripItem addingItem = e.Component as ToolStripItem; if (addingItem != null && addingItem.Owner != null) { @@ -1176,24 +1164,8 @@ protected override void Dispose(bool disposing) { _items = null; } - if (_undoEngine != null) - { - _undoEngine.Undoing -= new EventHandler(OnUndoing); - _undoEngine.Undone -= new EventHandler(OnUndone); - } - // unhook notifications. - if (_componentChangeSvc != null) - { - _componentChangeSvc.ComponentRemoved -= new ComponentEventHandler(ComponentChangeSvc_ComponentRemoved); - _componentChangeSvc.ComponentRemoving -= new ComponentEventHandler(ComponentChangeSvc_ComponentRemoving); - _componentChangeSvc.ComponentAdded -= new ComponentEventHandler(ComponentChangeSvc_ComponentAdded); - _componentChangeSvc.ComponentAdding -= new ComponentEventHandler(ComponentChangeSvc_ComponentAdding); - _componentChangeSvc.ComponentChanged -= new ComponentChangedEventHandler(ComponentChangeSvc_ComponentChanged); - } if (_selectionSvc != null) { - _selectionSvc.SelectionChanged -= new EventHandler(SelSvc_SelectionChanged); - _selectionSvc.SelectionChanging -= new EventHandler(SelSvc_SelectionChanging); _selectionSvc = null; } @@ -1227,22 +1199,6 @@ protected override void Dispose(bool disposing) _editorNode = null; } - if (ToolStrip != null) - { - ToolStrip.OverflowButton.DropDown.Closing -= new ToolStripDropDownClosingEventHandler(OnOverflowDropDownClosing); - ToolStrip.OverflowButton.DropDownOpening -= new EventHandler(OnOverFlowDropDownOpening); - ToolStrip.OverflowButton.DropDownOpened -= new EventHandler(OnOverFlowDropDownOpened); - ToolStrip.OverflowButton.DropDownClosed -= new EventHandler(OnOverFlowDropDownClosed); - ToolStrip.OverflowButton.DropDown.Resize -= new EventHandler(OnOverflowDropDownResize); - ToolStrip.OverflowButton.DropDown.Paint -= new PaintEventHandler(OnOverFlowDropDownPaint); - - ToolStrip.Move -= new System.EventHandler(OnToolStripMove); - ToolStrip.VisibleChanged -= new System.EventHandler(OnToolStripVisibleChanged); - ToolStrip.ItemAdded -= new ToolStripItemEventHandler(OnItemAdded); - ToolStrip.Resize -= new EventHandler(ToolStrip_Resize); - ToolStrip.DockChanged -= new EventHandler(ToolStrip_Resize); - ToolStrip.LayoutCompleted -= new EventHandler(ToolStrip_LayoutCompleted); - } // tear off the ContextMenu.. if (_toolStripContextMenu != null) { @@ -1342,14 +1298,14 @@ protected override ControlBodyGlyph GetControlGlyph(GlyphSelectionType selection if (menuEditorService == null || (menuEditorService != null && !menuEditorService.IsActive())) { - // now walk the winbar and add glyphs for each of it's children + // now walk the ToolStrip and add glyphs for each of it's children foreach (ToolStripItem item in ToolStrip.Items) { if (item is DesignerToolStripControlHost) { continue; } - // make sure it's on the winbar... + // make sure it's on the ToolStrip... if (item.Placement == ToolStripItemPlacement.Main) { ToolStripItemDesigner itemDesigner = (ToolStripItemDesigner)_host.GetDesigner(item); @@ -1468,46 +1424,16 @@ public override void Initialize(IComponent component) _componentChangeSvc = (IComponentChangeService)_host.GetService(typeof(IComponentChangeService)); } - if (_undoEngine == null) - { - _undoEngine = GetService(typeof(UndoEngine)) as UndoEngine; - if (_undoEngine != null) - { - _undoEngine.Undoing += new EventHandler(OnUndoing); - _undoEngine.Undone += new EventHandler(OnUndone); - } - } - - // initialize new Manager For Editing winbars + // initialize new Manager For Editing ToolStrips _editManager = new ToolStripEditorManager(component); // setup the dropdown if our handle has been created. if (Control.IsHandleCreated) { InitializeNewItemDropDown(); } - else - { - Control.HandleCreated += new EventHandler(Control_HandleCreated); - } - // attach notifcations. - if (_componentChangeSvc != null) - { - _componentChangeSvc.ComponentRemoved += new ComponentEventHandler(ComponentChangeSvc_ComponentRemoved); - _componentChangeSvc.ComponentRemoving += new ComponentEventHandler(ComponentChangeSvc_ComponentRemoving); - _componentChangeSvc.ComponentAdded += new ComponentEventHandler(ComponentChangeSvc_ComponentAdded); - _componentChangeSvc.ComponentAdding += new ComponentEventHandler(ComponentChangeSvc_ComponentAdding); - _componentChangeSvc.ComponentChanged += new ComponentChangedEventHandler(ComponentChangeSvc_ComponentChanged); - } //hookup to the AdornerService..for the overflow dropdown to be parent properly. _toolStripAdornerWindowService = (ToolStripAdornerWindowService)GetService(typeof(ToolStripAdornerWindowService)); - SelectionService.SelectionChanging += new EventHandler(SelSvc_SelectionChanging); - SelectionService.SelectionChanged += new EventHandler(SelSvc_SelectionChanged); - - // sink size changes. - ToolStrip.Resize += new EventHandler(ToolStrip_Resize); - ToolStrip.DockChanged += new EventHandler(ToolStrip_Resize); - ToolStrip.LayoutCompleted += new EventHandler(ToolStrip_LayoutCompleted); // Make sure the overflow is not toplevel ToolStrip.OverflowButton.DropDown.TopLevel = false; @@ -1683,7 +1609,7 @@ private void InitializeNewItemDropDown() } ToolStrip toolStrip = (ToolStrip)Component; AddNewTemplateNode(toolStrip); - // set up the right visibility state for the winbar. + // set up the right visibility state for the ToolStrip. SelSvc_SelectionChanged(null, EventArgs.Empty); } @@ -2053,11 +1979,9 @@ private void OnItemAdded(object sender, ToolStripItemEventArgs e) if (currentIndexOfEditor == -1 || currentIndexOfEditor != ToolStrip.Items.Count - 1) { // if the editor is not there or not at the end, add it to the end. - ToolStrip.ItemAdded -= new ToolStripItemEventHandler(OnItemAdded); ToolStrip.SuspendLayout(); ToolStrip.Items.Add(_editorNode); ToolStrip.ResumeLayout(); - ToolStrip.ItemAdded += new ToolStripItemEventHandler(OnItemAdded); } } LayoutToolStrip(); @@ -2316,7 +2240,7 @@ protected override void PreFilterProperties(IDictionary properties) /// private void RemoveBodyGlyphsForOverflow() { - // now walk the winbar and add glyphs for each of it's children + // now walk the ToolStrip and add glyphs for each of it's children foreach (ToolStripItem item in ToolStrip.Items) { if (item is DesignerToolStripControlHost) @@ -2379,7 +2303,7 @@ private void SetDragDropEffects(DragEventArgs de) } /// - /// When selection changes to the winbar, show the "AddItemsButton", when it leaves, hide it. + /// When selection changes to the ToolStrip, show the "AddItemsButton", when it leaves, hide it. /// private void SelSvc_SelectionChanging(object sender, EventArgs e) { @@ -2418,7 +2342,7 @@ private void SelSvc_SelectionChanging(object sender, EventArgs e) } /// - /// When selection changes to the winbar, show the "AddItemsButton", when it leaves, hide it. + /// When selection changes to the ToolStrip, show the "AddItemsButton", when it leaves, hide it. /// private void SelSvc_SelectionChanged(object sender, EventArgs e) { @@ -2462,20 +2386,12 @@ private void SelSvc_SelectionChanged(object sender, EventArgs e) ToolStripPanel parent = ToolStrip.Parent as ToolStripPanel; try { - if (parent != null) - { - parent.LocationChanged += new System.EventHandler(OnToolStripMove); - } FireSyncSelection = true; _editorNode.Visible = true; } finally { FireSyncSelection = originalSyncSelection; - if (parent != null) - { - parent.LocationChanged -= new System.EventHandler(OnToolStripMove); - } } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDropDownDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDropDownDesigner.cs index 8e13fc72814..c340dd338d3 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDropDownDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDropDownDesigner.cs @@ -25,7 +25,7 @@ internal class ToolStripDropDownDesigner : ComponentDesigner private ToolStripDropDown dropDown; private bool selected; private ControlBodyGlyph dummyToolStripGlyph; - private uint _editingCollection = 0; // non-zero if the collection editor is up for this winbar or a child of it. + private uint _editingCollection = 0; // non-zero if the collection editor is up for this ToolStrip or a child of it. MainMenu parentMenu = null; FormDocumentDesigner parentFormDesigner = null; internal ToolStripMenuItem currentParent = null; diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemDesigner.cs index 7dabbcda71d..4bbb8acf1d1 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemDesigner.cs @@ -33,7 +33,7 @@ internal class ToolStripItemDesigner : ComponentDesigner internal ControlBodyGlyph bodyGlyph = null; //bool which is set if we Add Dummy Item internal bool dummyItemAdded = false; - //Needed to Store the DRAGDROP Rect from the WinbarItemBehavior. + //Needed to Store the DRAGDROP Rect from the ToolStripItemBehavior. internal Rectangle dragBoxFromMouseDown = Rectangle.Empty; //defaulted to invalid index. this will be set by the behaviour. internal int indexOfItemUnderMouseToDrag = -1; diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs index 95e1af5e023..e26e8879848 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs @@ -1818,7 +1818,7 @@ public void RotateTab(bool backwards) } } ctl = currentSelection as Control; - //Added New Code for WinBar Tabbing.. + //Added New Code for ToolStrip Tabbing.. if (targetSelection == null && ctl is ToolStrip wb) { ToolStripItemCollection collection = wb.Items; diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs index 85ec14fa01d..554b23346da 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs @@ -1648,7 +1648,7 @@ internal override ToolStripItem MorphCurrentItem(Type t) } /// - /// Fired after a component has been added. Here, we add it to the winbar and select it. + /// Fired after a component has been added. Here, we add it to the ToolStrip and select it. /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] private void ComponentChangeSvc_ComponentAdded(object sender, ComponentEventArgs e) diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs index b16700b07ed..71b1d2a1257 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs @@ -14,7 +14,7 @@ namespace System.Windows.Forms.Design { /// - /// This internal class wraps the InSitu Editor. The editor is a runtime Winbar control which contains a leftButton (for image), centerLabel (for text) which gets swaped by a centerTextBox (when InSitu is ON). The ToolStripTemplateNode is also responsible for intercepting the Escape and Enter keys and implements the IMenuStatusHandler so that it can commit and rollback as required. Finally this ToolStripTemplateNode has a private class ItemTypeToolStripMenuItem for adding ToolStripItem types to the Dropdown for addItemButton. + /// This internal class wraps the InSitu Editor. The editor is a runtime ToolStrip control which contains a leftButton (for image), centerLabel (for text) which gets swaped by a centerTextBox (when InSitu is ON). The ToolStripTemplateNode is also responsible for intercepting the Escape and Enter keys and implements the IMenuStatusHandler so that it can commit and rollback as required. Finally this ToolStripTemplateNode has a private class ItemTypeToolStripMenuItem for adding ToolStripItem types to the Dropdown for addItemButton. /// internal class ToolStripTemplateNode : IMenuStatusHandler { @@ -1570,7 +1570,7 @@ protected override void WndProc(ref Message m) } /// - /// Private class to Change the Winbar to a TransparentWinbar. Our EditorToolStrip is a TranparentToolStrip so that it picks up the itemColor. + /// Private class to Change the ToolStrip to a TransparentToolStrip. Our EditorToolStrip is a TranparentToolStrip so that it picks up the itemColor. /// public class TransparentToolStrip : ToolStrip { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ImageIndexConverter.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ImageIndexConverter.cs index f76dfded7d3..51becdbe049 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ImageIndexConverter.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ImageIndexConverter.cs @@ -32,8 +32,8 @@ protected virtual bool IncludeNoneAsStandardValue { /// /// this is the property to look at when there is no ImageList property /// on the current object. For example, in ToolBarButton - the ImageList is - /// on the ToolBarButton.Parent property. In WinBarItem, the ImageList is on - /// the WinBarItem.Owner property. + /// on the ToolBarButton.Parent property. In ToolStripItem, the ImageList is on + /// the ToolStripItem.Owner property. /// internal string ParentImageListProperty { get { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ImageKeyConverter.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ImageKeyConverter.cs index 3e1987d30b6..5ad8a328ab7 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ImageKeyConverter.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ImageKeyConverter.cs @@ -33,8 +33,8 @@ protected virtual bool IncludeNoneAsStandardValue { /// /// this is the property to look at when there is no ImageList property /// on the current object. For example, in ToolBarButton - the ImageList is - /// on the ToolBarButton.Parent property. In WinBarItem, the ImageList is on - /// the WinBarItem.Owner property. + /// on the ToolBarButton.Parent property. In ToolStripItem, the ImageList is on + /// the ToolStripItem.Owner property. /// internal string ParentImageListProperty { get { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/SplitContainer.cs b/src/System.Windows.Forms/src/System/Windows/Forms/SplitContainer.cs index 28a894b5578..cf8fb43eec9 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/SplitContainer.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/SplitContainer.cs @@ -394,7 +394,7 @@ public BorderStyle BorderStyle { } } - /// + /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event ControlEventHandler ControlAdded { add { @@ -404,7 +404,7 @@ public BorderStyle BorderStyle { base.ControlAdded -= value; } } - /// + /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event ControlEventHandler ControlRemoved { add { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStrip.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStrip.cs index 4303713ccab..e2b6ad346e0 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStrip.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStrip.cs @@ -784,7 +784,7 @@ internal virtual NativeWindow DropDownOwnerWindow { /// /// Returns the drop target manager that all the hwndless - /// items and this winbar share. this is necessary as + /// items and this ToolStrip share. this is necessary as /// RegisterDragDrop requires an HWND. /// internal ToolStripDropTargetManager DropTargetManager { @@ -852,7 +852,7 @@ public override Rectangle DisplayRectangle { /// /// /// - /// Forecolor really has no meaning for winbars - so lets hide it + /// Forecolor really has no meaning for ToolStrips - so lets hide it /// [Browsable(false)] public new Color ForeColor { @@ -949,7 +949,7 @@ public ToolStripGripDisplayStyle GripDisplayStyle { /// /// - /// The external spacing between the grip and the padding of the winbar and the first item in the collection + /// The external spacing between the grip and the padding of the ToolStrip and the first item in the collection /// [ SRCategory(nameof(SR.CatLayout)), @@ -966,7 +966,7 @@ public Padding GripMargin { /// /// - /// The boundaries of the grip on the winbar. If it is invisible - returns Rectangle.Empty. + /// The boundaries of the grip on the ToolStrip. If it is invisible - returns Rectangle.Empty. /// [ Browsable(false) @@ -1139,7 +1139,7 @@ private bool IsLocationChanging { /// The items that belong to this ToolStrip. /// Note - depending on space and layout preferences, not all items /// in this collection will be displayed. They may not even be displayed - /// on this winbar (say in the case where we're overflowing the item). + /// on this ToolStrip (say in the case where we're overflowing the item). /// The collection of _Displayed_ items is the DisplayedItems collection. /// The displayed items collection also includes things like the OverflowButton /// and the Grip. @@ -1220,7 +1220,7 @@ public event ToolStripItemEventHandler ItemRemoved { Events.RemoveHandler(EventItemRemoved, value); } } - /// + /// /// handy check for painting and sizing [Browsable(false)] public bool IsDropDown { @@ -1235,7 +1235,7 @@ internal bool IsDisposingItems { /// /// The OnDrag[blah] methods that will be called if AllowItemReorder is true. /// - /// This allows us to have methods that handle drag/drop of the winbar items + /// This allows us to have methods that handle drag/drop of the ToolStrip items /// without calling back on the user's code /// internal IDropTarget ItemReorderDropTarget { @@ -1251,7 +1251,7 @@ internal IDropTarget ItemReorderDropTarget { /// The OnQueryContinueDrag and OnGiveFeedback methods that will be called if /// AllowItemReorder is true. /// - /// This allows us to have methods that handle drag/drop of the winbar items + /// This allows us to have methods that handle drag/drop of the ToolStrip items /// without calling back on the user's code /// internal ISupportOleDropSource ItemReorderDropSource { @@ -1625,7 +1625,7 @@ internal override bool SupportsUiaProviders { /// /// - /// The renderer is used to paint the hwndless winbar items. If someone wanted to + /// The renderer is used to paint the hwndless ToolStrip items. If someone wanted to /// change the "Hot" look of all of their buttons to be a green triangle, they should /// create a class that derives from ToolStripRenderer, assign it to this property and call /// invalidate. @@ -2650,7 +2650,7 @@ private void HookStaticEvents(bool hook) { } } - //initialize winbar + //initialize ToolStrip private void InitializeRenderer(ToolStripRenderer renderer) { // wrap this in a LayoutTransaction so that if they change sizes // in this method we've suspended layout. @@ -2664,7 +2664,7 @@ private void InitializeRenderer(ToolStripRenderer renderer) { } - // sometimes you only want to force a layout if the winbar is visible. + // sometimes you only want to force a layout if the ToolStrip is visible. private void InvalidateLayout() { if (IsHandleCreated) { LayoutTransaction.DoLayout(this, this, null); @@ -3322,7 +3322,7 @@ protected internal virtual void OnItemAdded(ToolStripItemEventArgs e) { /// /// - /// Called when an item has been clicked on the winbar. + /// Called when an item has been clicked on the ToolStrip. /// protected virtual void OnItemClicked(ToolStripItemClickedEventArgs e) { ToolStripItemClickedEventHandler handler = (ToolStripItemClickedEventHandler)Events[EventItemClicked]; @@ -3418,7 +3418,7 @@ internal virtual void OnLocationChanging(ToolStripLocationCancelEventArgs e) { /// /// - /// Delegate mouse down to the winbar and its affected items + /// Delegate mouse down to the ToolStrip and its affected items /// protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs mea) { @@ -3453,7 +3453,7 @@ protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs mea) { /// /// - /// Delegate mouse moves to the winbar and its affected items + /// Delegate mouse moves to the ToolStrip and its affected items /// protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs mea) { Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose,"OnMouseMove called"); @@ -3516,7 +3516,7 @@ protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs mea) { /// /// - /// Delegate mouse leave to the winbar and its affected items + /// Delegate mouse leave to the ToolStrip and its affected items /// protected override void OnMouseLeave(System.EventArgs e) { HandleMouseLeave(); @@ -3539,7 +3539,7 @@ protected override void OnMouseCaptureChanged(System.EventArgs e) { /// /// - /// Delegate mouse up to the winbar and its affected items + /// Delegate mouse up to the ToolStrip and its affected items /// protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs mea) { @@ -4024,7 +4024,7 @@ internal virtual void ResetRenderMode() { RenderMode = ToolStripRenderMode.ManagerRenderMode; } - /// + /// [EditorBrowsable(EditorBrowsableState.Never)] public void ResetMinimumSize() { CommonProperties.SetMinimumSize(this, new Size(-1,-1)); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripComboBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripComboBox.cs index 3845fcdaf10..50fbfd33309 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripComboBox.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripComboBox.cs @@ -153,7 +153,7 @@ private static Control CreateControlInstance() { comboBox.Font = ToolStripManager.DefaultFont; return comboBox; } - /// + /// [ DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Localizable(true), @@ -166,7 +166,7 @@ public System.Windows.Forms.AutoCompleteStringCollection AutoCompleteCustomSourc set { ComboBox.AutoCompleteCustomSource = value;} } - /// + /// [ DefaultValue(AutoCompleteMode.None), SRDescription(nameof(SR.ComboBoxAutoCompleteModeDescr)), @@ -177,7 +177,7 @@ public AutoCompleteMode AutoCompleteMode { set { ComboBox.AutoCompleteMode = value;} } - /// + /// [ DefaultValue(AutoCompleteSource.None), SRDescription(nameof(SR.ComboBoxAutoCompleteSourceDescr)), @@ -259,7 +259,7 @@ protected internal override Padding DefaultMargin { } } - /// + /// [SRCategory(nameof(SR.CatBehavior)), SRDescription(nameof(SR.ComboBoxOnDropDownDescr))] public event EventHandler DropDown { add { @@ -270,7 +270,7 @@ public event EventHandler DropDown { } } - /// + /// [SRCategory(nameof(SR.CatBehavior)), SRDescription(nameof(SR.ComboBoxOnDropDownClosedDescr))] public event EventHandler DropDownClosed { add { @@ -280,7 +280,7 @@ public event EventHandler DropDownClosed { Events.RemoveHandler(EventDropDownClosed, value); } } - /// + /// [SRCategory(nameof(SR.CatBehavior)), SRDescription(nameof(SR.ComboBoxDropDownStyleChangedDescr))] public event EventHandler DropDownStyleChanged { add { @@ -292,7 +292,7 @@ public event EventHandler DropDownStyleChanged { } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), SRDescription(nameof(SR.ComboBoxDropDownHeightDescr)), @@ -305,7 +305,7 @@ public int DropDownHeight { } - /// + /// [ SRCategory(nameof(SR.CatAppearance)), DefaultValue(ComboBoxStyle.DropDown), @@ -317,7 +317,7 @@ public ComboBoxStyle DropDownStyle { set { ComboBox.DropDownStyle = value;} } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), SRDescription(nameof(SR.ComboBoxDropDownWidthDescr)) @@ -327,7 +327,7 @@ public int DropDownWidth { set { ComboBox.DropDownWidth = value;} } - /// + /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), @@ -338,7 +338,7 @@ public bool DroppedDown { set { ComboBox.DroppedDown = value;} } - /// + /// [ SRCategory(nameof(SR.CatAppearance)), DefaultValue(FlatStyle.Popup), @@ -350,7 +350,7 @@ public FlatStyle FlatStyle { set { ComboBox.FlatStyle = value;} } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), DefaultValue(true), @@ -378,7 +378,7 @@ public ComboBox.ObjectCollection Items { } } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), DefaultValue(8), @@ -389,7 +389,7 @@ public int MaxDropDownItems { get { return ComboBox.MaxDropDownItems; } set { ComboBox.MaxDropDownItems = value;} } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), DefaultValue(0), @@ -401,7 +401,7 @@ public int MaxLength { set { ComboBox.MaxLength = value; } } - /// + /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), @@ -411,7 +411,7 @@ public int SelectedIndex { get { return ComboBox.SelectedIndex; } set { ComboBox.SelectedIndex = value;} } - /// + /// [SRCategory(nameof(SR.CatBehavior)), SRDescription(nameof(SR.selectedIndexChangedEventDescr))] public event EventHandler SelectedIndexChanged { add { @@ -421,7 +421,7 @@ public event EventHandler SelectedIndexChanged { Events.RemoveHandler(EventSelectedIndexChanged, value); } } - /// + /// [ Browsable(false), Bindable(true), @@ -433,7 +433,7 @@ public object SelectedItem { set { ComboBox.SelectedItem = value;} } - /// + /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), @@ -443,7 +443,7 @@ public string SelectedText { get { return ComboBox.SelectedText; } set { ComboBox.SelectedText = value;} } - /// + /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), @@ -454,7 +454,7 @@ public int SelectionLength { set { ComboBox.SelectionLength = value;} } - /// + /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), @@ -464,7 +464,7 @@ public int SelectionStart { get { return ComboBox.SelectionStart; } set { ComboBox.SelectionStart = value;} } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), DefaultValue(false), @@ -476,7 +476,7 @@ public bool Sorted { } - /// + /// [SRCategory(nameof(SR.CatBehavior)), SRDescription(nameof(SR.ComboBoxOnTextUpdateDescr))] public event EventHandler TextUpdate { add { @@ -489,23 +489,23 @@ public event EventHandler TextUpdate { #region WrappedMethods - /// + /// public void BeginUpdate() { ComboBox.BeginUpdate(); } - /// + /// public void EndUpdate() { ComboBox.EndUpdate(); } - /// + /// public int FindString(string s) { return ComboBox.FindString(s); } - /// + /// public int FindString(string s, int startIndex) { return ComboBox.FindString(s, startIndex); } - /// + /// public int FindStringExact(string s) { return ComboBox.FindStringExact(s); } - /// + /// public int FindStringExact(string s, int startIndex) { return ComboBox.FindStringExact(s, startIndex); } - /// + /// public int GetItemHeight(int index) { return ComboBox.GetItemHeight(index); } - /// + /// public void Select(int start, int length) { ComboBox.Select(start, length); } - /// + /// public void SelectAll() { ComboBox.SelectAll(); } #endregion WrappedMethods @@ -539,7 +539,7 @@ private void HandleTextUpdate(object sender, System.EventArgs e) { OnTextUpdate(e); } - /// + /// protected virtual void OnDropDown(EventArgs e) { if (ParentInternal != null) { Application.ThreadContext.FromCurrent().RemoveMessageFilter(ParentInternal.RestoreFocusFilter); @@ -547,7 +547,7 @@ protected virtual void OnDropDown(EventArgs e) { } RaiseEvent(EventDropDown, e); } - /// + /// protected virtual void OnDropDownClosed(EventArgs e) { if (ParentInternal != null) { // PERF, @@ -557,24 +557,24 @@ protected virtual void OnDropDownClosed(EventArgs e) { } RaiseEvent(EventDropDownClosed, e); } - /// + /// protected virtual void OnDropDownStyleChanged(EventArgs e) { RaiseEvent(EventDropDownStyleChanged, e); } - /// + /// protected virtual void OnSelectedIndexChanged(EventArgs e) { RaiseEvent(EventSelectedIndexChanged, e); } - /// + /// protected virtual void OnSelectionChangeCommitted(EventArgs e) { RaiseEvent(EventSelectionChangeCommitted, e); } - /// + /// protected virtual void OnTextUpdate(EventArgs e) { RaiseEvent(EventTextUpdate, e); } - /// + /// protected override void OnSubscribeControlEvents(Control control) { ComboBox comboBox = control as ComboBox; if (comboBox != null) { @@ -591,7 +591,7 @@ protected override void OnSubscribeControlEvents(Control control) { base.OnSubscribeControlEvents(control); } - /// + /// protected override void OnUnsubscribeControlEvents(Control control) { ComboBox comboBox = control as ComboBox; if (comboBox != null) { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripControlHost.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripControlHost.cs index e84080a7f61..54b83a4aa61 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripControlHost.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripControlHost.cs @@ -371,7 +371,7 @@ public event EventHandler LostFocus { } - /// + /// /// /// Occurs when a key is pressed down while the control has focus. /// @@ -385,7 +385,7 @@ public event KeyEventHandler KeyDown { } } - /// + /// /// /// Occurs when a key is pressed while the control has focus. /// @@ -399,7 +399,7 @@ public event KeyPressEventHandler KeyPress { } } - /// + /// /// /// Occurs when a key is released while the control has focus. /// @@ -780,15 +780,15 @@ protected virtual void OnLeave(EventArgs e) { protected virtual void OnLostFocus(EventArgs e) { RaiseEvent(EventLostFocus, e); } - /// + /// protected virtual void OnKeyDown(KeyEventArgs e) { RaiseKeyEvent(EventKeyDown, e); } - /// + /// protected virtual void OnKeyPress(KeyPressEventArgs e) { RaiseKeyPressEvent(EventKeyPress, e); } - /// + /// protected virtual void OnKeyUp(KeyEventArgs e) { RaiseKeyEvent(EventKeyUp, e); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDown.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDown.cs index 57dba0700bd..8cd9d2d8eb1 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDown.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDown.cs @@ -298,7 +298,7 @@ public bool AutoClose { remove { base.ContextMenuStripChanged -= value; } } - /// + /// /// /// /// @@ -964,7 +964,7 @@ protected virtual bool TopMost } - /// + /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool TopLevel { get { @@ -985,7 +985,7 @@ public bool TopLevel { // it's not robust enough for our needs // } - /// + /// /// Override base TabIndex property in order to avoid serialization /// (since a dropdown shouldn't participate in the taborder...) [ @@ -1029,7 +1029,7 @@ public bool TopLevel { remove { base.Validating -= value; } } - /// + /// /// Override base Visible property in order to control serialization by setting default value [ SRCategory(nameof(SR.CatBehavior)), @@ -1083,7 +1083,7 @@ private void ApplyTopMost(bool topMost) { } } - /// + /// protected override void Dispose(bool disposing) { if (disposing) { SourceControlInternal = null; @@ -1322,7 +1322,7 @@ protected virtual void OnClosing(ToolStripDropDownClosingEventArgs e) { ToolStripDropDownClosingEventHandler handler = (ToolStripDropDownClosingEventHandler)Events[EventClosing]; if (handler != null) handler(this, e); } - /// + /// /// /// When our handle is being created, suspend the deactivation /// portion of the WndProc, as we'll never be shown. @@ -1415,7 +1415,7 @@ protected override void OnVisibleChanged(System.EventArgs e) { } } - /// + /// protected override void OnParentChanged(System.EventArgs e) { base.OnParentChanged(e); Rectangle bounds = this.Bounds; diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDownButton.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDownButton.cs index b5ec8a00602..944032c3ecf 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDownButton.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDownButton.cs @@ -104,7 +104,7 @@ internal override ToolStripItemInternalLayout CreateInternalLayout() { protected override ToolStripDropDown CreateDefaultDropDown() { - // AutoGenerate a Winbar DropDown - set the property so we hook events + // AutoGenerate a ToolStrip DropDown - set the property so we hook events return new ToolStripDropDownMenu(this, /*isAutoGenerated=*/true); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDownItem.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDownItem.cs index 7ad9189570f..846b5a5fd3c 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDownItem.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDownItem.cs @@ -196,7 +196,7 @@ internal protected virtual Point DropDownLocation } } - /// + /// [ SRCategory(nameof(SR.CatAction)), SRDescription(nameof(SR.ToolStripDropDownOpeningDescr)) @@ -300,7 +300,7 @@ protected override AccessibleObject CreateAccessibilityInstance() { /// protected virtual ToolStripDropDown CreateDefaultDropDown() { - // AutoGenerate a Winbar DropDown - set the property so we hook events + // AutoGenerate a ToolStrip DropDown - set the property so we hook events return new ToolStripDropDown(this, true); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDownMenu.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDownMenu.cs index f26d0668fec..e9025aa1d82 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDownMenu.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropDownMenu.cs @@ -212,7 +212,7 @@ protected internal override Size MaxItemSize { } } - /// + /// [ DefaultValue(true), SRDescription(nameof(SR.ToolStripDropDownMenuShowImageMarginDescr)), @@ -230,7 +230,7 @@ public bool ShowImageMargin { } } - /// + /// [ DefaultValue(false), SRDescription(nameof(SR.ToolStripDropDownMenuShowCheckMarginDescr)), diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropTargetManager.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropTargetManager.cs index a14d7b05d35..9d7b04d6c12 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropTargetManager.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripDropTargetManager.cs @@ -108,7 +108,7 @@ public void OnDragEnter(DragEventArgs e) { lastDropTarget = ((IDropTarget)item); } else if (owner.AllowDrop) { - // the winbar wants this event + // the ToolStrip wants this event Debug.WriteLineIf(DragDropDebug.TraceVerbose, "ToolStrip taking this because AllowDrop set to true."); lastDropTarget = ((IDropTarget)owner); @@ -162,7 +162,7 @@ public void OnDragOver(DragEventArgs e) { newDropTarget = ((IDropTarget)item); } else if (owner.AllowDrop) { - // the winbar wants this event + // the ToolStrip wants this event Debug.WriteLineIf(DragDropDebug.TraceVerbose, "ToolStrip taking this because AllowDrop set to true."); newDropTarget = ((IDropTarget)owner); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripGripRenderEventHandler.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripGripRenderEventHandler.cs index 4def4acb7a2..9db51b83be5 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripGripRenderEventHandler.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripGripRenderEventHandler.cs @@ -5,7 +5,7 @@ namespace System.Windows.Forms { /// - /// Called when the background of a winbar item is being rendered + /// Called when the background of a ToolStrip item is being rendered /// public delegate void ToolStripGripRenderEventHandler(object sender, ToolStripGripRenderEventArgs e); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItem.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItem.cs index a5386ada1a1..0074e49556c 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItem.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItem.cs @@ -327,7 +327,7 @@ public AccessibleRole AccessibleRole { /// /// - /// Determines if the item aligns towards the beginning or end of the winbar. + /// Determines if the item aligns towards the beginning or end of the ToolStrip. /// [ DefaultValue(ToolStripItemAlignment.Left), @@ -874,7 +874,7 @@ public event EventHandler DragLeave { /// /// This represents what we're actually going to drag. If the parent has set AllowItemReorder to true, /// then the item should call back on the private OnQueryContinueDrag/OnGiveFeedback that is implemented - /// in the parent winbar. + /// in the parent ToolStrip. /// /// Else if the parent does not support reordering of items (Parent.AllowItemReorder = false) - /// then call back on the ToolStripItem's OnQueryContinueDrag/OnGiveFeedback methods. @@ -1221,7 +1221,7 @@ public virtual Image Image { } - /// + /// [ Localizable(true), SRCategory(nameof(SR.CatAppearance)), @@ -1745,8 +1745,8 @@ public event PaintEventHandler Paint { /// /// The parent of this ToolStripItem. This can be distinct from the owner because /// the item can fall onto another window (overflow). In this case the overflow - /// would be the parent but the original winbar would be the Owner. The "parent" - /// winbar will be firing things like paint events - where as the "owner" winbar + /// would be the parent but the original ToolStrip would be the Owner. The "parent" + /// ToolStrip will be firing things like paint events - where as the "owner" ToolStrip /// will be containing shared data like image lists. Typically the only one who should /// set the parent property is the layout manager on the ToolStrip. /// @@ -1768,7 +1768,7 @@ internal protected ToolStrip Parent { /// /// - /// Specifies whether or not the item is glued to the winbar or overflow or + /// Specifies whether or not the item is glued to the ToolStrip or overflow or /// can float between the two. /// [ @@ -1814,7 +1814,7 @@ public virtual Padding Padding { } /// - /// This is explicitly a winbar, because only winbars know how to manage winbaritems + /// This is explicitly a ToolStrip, because only ToolStrips know how to manage ToolStripitems /// internal ToolStrip ParentInternal { get { @@ -2445,7 +2445,7 @@ internal virtual ToolStripItemInternalLayout CreateInternalLayout() { } /// /// - /// Disposes this winbar item... + /// Disposes this ToolStrip item... /// protected override void Dispose(bool disposing) { @@ -4587,7 +4587,7 @@ private ButtonBaseAdapter.LayoutData GetLayoutData() { public virtual Size GetPreferredSize(Size constrainingSize) { Size preferredSize = Size.Empty; EnsureLayout(); - // we would prefer not to be larger than the winbar itself. + // we would prefer not to be larger than the ToolStrip itself. // so we'll ask the ButtonAdapter layout guy what it thinks // its preferred size should be - and we'll tell it to be no // bigger than the ToolStrip itself. Note this is "Parent" not diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemEventType.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemEventType.cs index 8c8a770eca6..361d831c2c5 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemEventType.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemEventType.cs @@ -6,7 +6,7 @@ namespace System.Windows.Forms { /// /// These methods allow the ToolStrip to route events - /// to the winbar item. Since a ToolStrip is not a ToolStripItem, + /// to the ToolStrip item. Since a ToolStrip is not a ToolStripItem, /// it cannot directly call OnPaint. /// internal enum ToolStripItemEventType diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemImageRenderEventArgs.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemImageRenderEventArgs.cs index 77985c1f7ee..ff30011abd9 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemImageRenderEventArgs.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemImageRenderEventArgs.cs @@ -16,7 +16,7 @@ public ToolStripItemImageRenderEventArgs(Graphics g, ToolStripItem item, Rectang } /// - /// This class represents all the information to render the winbar + /// This class represents all the information to render the ToolStrip /// public ToolStripItemImageRenderEventArgs(Graphics g, ToolStripItem item, Image image, Rectangle imageRectangle) : base(g, item) { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemImageRenderEventHandler.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemImageRenderEventHandler.cs index eef228db0d6..431979b5744 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemImageRenderEventHandler.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemImageRenderEventHandler.cs @@ -5,7 +5,7 @@ namespace System.Windows.Forms { /// - /// Called when the background of a winbar item is being rendered + /// Called when the background of a ToolStrip item is being rendered /// public delegate void ToolStripItemImageRenderEventHandler(object sender, ToolStripItemImageRenderEventArgs e); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemOverflow.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemOverflow.cs index 379a296a286..20431e28996 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemOverflow.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemOverflow.cs @@ -9,7 +9,7 @@ namespace System.Windows.Forms /// public enum ToolStripItemOverflow { - Never, // on the main winbar itself, + Never, // on the main ToolStrip itself, Always, // on the overflow window AsNeeded // DEFAULT try for main, overflow as necessary } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemPlacement.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemPlacement.cs index 6905bde34d5..4cc07d4e74c 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemPlacement.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemPlacement.cs @@ -9,7 +9,7 @@ namespace System.Windows.Forms /// public enum ToolStripItemPlacement { - Main, // in the main winbar itself + Main, // in the main ToolStrip itself Overflow, // in the overflow window None // either offscreen or visible == false so we didn't lay it out } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemRenderEventArgs.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemRenderEventArgs.cs index c782b447166..8754eb07f3e 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemRenderEventArgs.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemRenderEventArgs.cs @@ -9,7 +9,7 @@ namespace System.Windows.Forms public class ToolStripItemRenderEventArgs : EventArgs { /// - /// This class represents all the information to render the winbar + /// This class represents all the information to render the ToolStrip /// public ToolStripItemRenderEventArgs(Graphics g, ToolStripItem item) { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemRenderEventHandler.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemRenderEventHandler.cs index db226838d81..f15a606ae5a 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemRenderEventHandler.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemRenderEventHandler.cs @@ -5,7 +5,7 @@ namespace System.Windows.Forms { /// - /// Called when the background of a winbar item is being rendered + /// Called when the background of a ToolStrip item is being rendered /// public delegate void ToolStripItemRenderEventHandler(object sender, ToolStripItemRenderEventArgs e); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemTextRenderEventArgs.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemTextRenderEventArgs.cs index 4b5ad5b13ca..fb02c0fb1e4 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemTextRenderEventArgs.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemTextRenderEventArgs.cs @@ -7,7 +7,7 @@ namespace System.Windows.Forms { /// - /// This class represents all the information to render the winbar + /// This class represents all the information to render the ToolStrip /// public class ToolStripItemTextRenderEventArgs : ToolStripItemRenderEventArgs { @@ -16,7 +16,7 @@ public class ToolStripItemTextRenderEventArgs : ToolStripItemRenderEventArgs private bool _textColorChanged = false; /// - /// This class represents all the information to render the winbar + /// This class represents all the information to render the ToolStrip /// public ToolStripItemTextRenderEventArgs(Graphics g, ToolStripItem item, string text, Rectangle textRectangle, Color textColor, Font textFont, TextFormatFlags format) : base(g, item) { @@ -30,7 +30,7 @@ public ToolStripItemTextRenderEventArgs(Graphics g, ToolStripItem item, string t } /// - /// This class represents all the information to render the winbar + /// This class represents all the information to render the ToolStrip /// public ToolStripItemTextRenderEventArgs(Graphics g, ToolStripItem item, string text, Rectangle textRectangle, Color textColor, Font textFont, ContentAlignment textAlign) : base(g, item) { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemTextRenderEventHandler.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemTextRenderEventHandler.cs index 56b0f99b5fe..3c1e2ab13dc 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemTextRenderEventHandler.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItemTextRenderEventHandler.cs @@ -5,7 +5,7 @@ namespace System.Windows.Forms { /// - /// Called when the background of a winbar item is being rendered + /// Called when the background of a ToolStrip item is being rendered /// public delegate void ToolStripItemTextRenderEventHandler(object sender, ToolStripItemTextRenderEventArgs e); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripLabel.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripLabel.cs index 59633793b9d..ffa6a9c2c4e 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripLabel.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripLabel.cs @@ -14,7 +14,7 @@ namespace System.Windows.Forms { /// /// - /// A non selectable winbar item + /// A non selectable ToolStrip item /// [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip)] public class ToolStripLabel : ToolStripItem { @@ -33,7 +33,7 @@ public class ToolStripLabel : ToolStripItem { /// /// - /// A non selectable winbar item + /// A non selectable ToolStrip item /// public ToolStripLabel() { } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripMenuItem.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripMenuItem.cs index 291c1991c20..ec2a234b74e 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripMenuItem.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripMenuItem.cs @@ -159,7 +159,7 @@ private void ClearShortcutCache() { /// protected override ToolStripDropDown CreateDefaultDropDown() { - // AutoGenerate a Winbar DropDown - set the property so we hook events + // AutoGenerate a ToolStrip DropDown - set the property so we hook events return new ToolStripDropDownMenu(this, true); } @@ -196,13 +196,13 @@ protected override Size DefaultSize { } - /// + /// protected internal override Padding DefaultMargin { get { return Padding.Empty; } } - /// + /// protected override Padding DefaultPadding { get { if (IsOnDropDown) { @@ -353,7 +353,7 @@ private static Bitmap GetBitmapFromIcon(string iconName, Size desiredIconSize) return b; } - /// + /// [ DefaultValue(false), SRCategory(nameof(SR.CatBehavior)), @@ -436,7 +436,7 @@ public event EventHandler CheckStateChanged { /// /// - /// Specifies whether or not the item is glued to the winbar or overflow or + /// Specifies whether or not the item is glued to the ToolStrip or overflow or /// can float between the two. /// [ @@ -838,7 +838,7 @@ internal void HandleAutoExpansion() { } } - /// + /// protected override void OnClick(EventArgs e) { if (checkOnClick) { this.Checked = !this.Checked; @@ -1102,7 +1102,7 @@ protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { else { - // Toplevel item support, menu items hosted on a plain winbar dropdown + // Toplevel item support, menu items hosted on a plain ToolStrip dropdown if ((DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) { renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(g, this, this.Text, InternalLayout.TextRectangle, textColor, this.Font, InternalLayout.TextFormat)); } @@ -1152,7 +1152,7 @@ protected internal override bool ProcessMnemonic(char charCode) { return base.ProcessMnemonic(charCode); } - /// + /// /// overridden here so we scooch over when we're in the ToolStripDropDownMenu internal protected override void SetBounds(Rectangle rect) { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripOverflow.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripOverflow.cs index 02a29f929d5..8e3cad6fc77 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripOverflow.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripOverflow.cs @@ -144,7 +144,7 @@ protected override void OnLayout(LayoutEventArgs e) { /// protected override void SetDisplayedItems() { // do nothing here.... this is really for the setting the overflow/displayed items on the - // main winbar. Our working item collection is our displayed item collection... calling + // main ToolStrip. Our working item collection is our displayed item collection... calling // base would clear it out. Size biggestItemSize = Size.Empty; for (int j = 0; j < DisplayedItems.Count; j++) { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripOverflowButton.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripOverflowButton.cs index 9b95c5884f3..39211d90526 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripOverflowButton.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripOverflowButton.cs @@ -53,7 +53,7 @@ protected override void Dispose(bool disposing) { base.Dispose(disposing); } - /// + /// protected internal override Padding DefaultMargin { get { return Padding.Empty; @@ -96,7 +96,7 @@ protected override AccessibleObject CreateAccessibilityInstance() { /// protected override ToolStripDropDown CreateDefaultDropDown() { - // AutoGenerate a Winbar DropDown - set the property so we hook events + // AutoGenerate a ToolStrip DropDown - set the property so we hook events return new ToolStripOverflow(this); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripPanelRow.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripPanelRow.cs index dc018c052e1..8212bda6028 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripPanelRow.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripPanelRow.cs @@ -692,7 +692,7 @@ void IArrangedElement.PerformLayout(IArrangedElement container, string propertyN #region MouseStuff #if DEBUG - internal static readonly TraceSwitch ToolStripPanelMouseDebug = new TraceSwitch("ToolStripPanelMouse", "Debug WinBar WM_MOUSEACTIVATE code"); + internal static readonly TraceSwitch ToolStripPanelMouseDebug = new TraceSwitch("ToolStripPanelMouse", "Debug ToolStrip WM_MOUSEACTIVATE code"); #else internal static readonly TraceSwitch ToolStripPanelMouseDebug; #endif diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripRenderEventHandler.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripRenderEventHandler.cs index b3695ad9722..29dbfc93b3b 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripRenderEventHandler.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripRenderEventHandler.cs @@ -5,7 +5,7 @@ namespace System.Windows.Forms { /// - /// Called when the background of the winbar is being rendered + /// Called when the background of the ToolStrip is being rendered /// public delegate void ToolStripRenderEventHandler(object sender, ToolStripRenderEventArgs e); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripRenderer.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripRenderer.cs index c48183708bd..f1a2da79729 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripRenderer.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripRenderer.cs @@ -131,7 +131,7 @@ internal virtual ToolStripRenderer RendererOverride { - /// + /// public event ToolStripArrowRenderEventHandler RenderArrow { add { AddHandler(EventRenderArrow, value); @@ -259,7 +259,7 @@ public event ToolStripItemImageRenderEventHandler RenderItemImage { RemoveHandler(EventRenderItemImage, value); } } - /// + /// /// /// Draws the checkmark /// @@ -317,7 +317,7 @@ public event ToolStripItemRenderEventHandler RenderMenuItemBackground { } } - /// + /// /// /// Draws the split button /// @@ -357,7 +357,7 @@ public event ToolStripItemRenderEventHandler RenderSplitButtonBackground { } - /// + /// public event ToolStripSeparatorRenderEventHandler RenderSeparator { add { AddHandler(EventRenderSeparator, value); @@ -388,7 +388,7 @@ public static Image CreateDisabledImage(Image normalImage) { return CreateDisabledImage(normalImage, null); } - /// + /// public void DrawArrow(ToolStripArrowRenderEventArgs e) { OnRenderArrow(e); @@ -534,7 +534,7 @@ public void DrawItemImage(ToolStripItemImageRenderEventArgs e) { } } - /// + /// /// /// Draw image /// @@ -590,7 +590,7 @@ public void DrawSplitButton(ToolStripItemRenderEventArgs e) { } - /// + /// /// /// Draw the background color /// @@ -681,7 +681,7 @@ protected static void ScaleArrowOffsetsIfNeeded() { } - /// + /// protected virtual void OnRenderArrow(ToolStripArrowRenderEventArgs e){ if (RendererOverride != null) { @@ -740,7 +740,7 @@ protected virtual void OnRenderArrow(ToolStripArrowRenderEventArgs e){ /// /// - /// Draw the winbar background. ToolStrip users should override this if they want to draw differently. + /// Draw the ToolStrip background. ToolStrip users should override this if they want to draw differently. /// protected virtual void OnRenderToolStripBackground(ToolStripRenderEventArgs e) { if (RendererOverride != null) { @@ -866,7 +866,7 @@ protected virtual void OnRenderItemImage(ToolStripItemImageRenderEventArgs e) { } - /// + /// protected virtual void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) { if (RendererOverride != null) { RendererOverride.OnRenderItemCheck(e); @@ -977,7 +977,7 @@ protected virtual void OnRenderToolStripContentPanelBackground(ToolStripContentP } } - /// + /// protected virtual void OnRenderToolStripStatusLabelBackground(ToolStripItemRenderEventArgs e) { if (RendererOverride != null) { RendererOverride.OnRenderToolStripStatusLabelBackground(e); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripScrollButton.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripScrollButton.cs index 246b94fec8c..ef951341c97 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripScrollButton.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripScrollButton.cs @@ -13,7 +13,7 @@ namespace System.Windows.Forms { /// /// - /// A non selectable winbar item + /// A non selectable ToolStrip item /// internal class ToolStripScrollButton : ToolStripControlHost { private bool up = true; diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSeparator.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSeparator.cs index cd7e8a5cc7f..9d0debde2ae 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSeparator.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSeparator.cs @@ -13,13 +13,13 @@ namespace System.Windows.Forms { /// /// /// - /// Called when the background of the winbar is being rendered + /// Called when the background of the ToolStrip is being rendered /// /// [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.ContextMenuStrip)] public class ToolStripSeparator : ToolStripItem { - private const int WINBAR_SEPARATORTHICKNESS = 6; - private const int WINBAR_SEPARATORHEIGHT = 23; + private const int ToolStrip_SEPARATORTHICKNESS = 6; + private const int ToolStrip_SEPARATORHEIGHT = 23; [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] public ToolStripSeparator() { @@ -88,12 +88,12 @@ public override bool CanSelect { /// protected override Size DefaultSize { get { - return new Size(WINBAR_SEPARATORTHICKNESS, WINBAR_SEPARATORTHICKNESS); + return new Size(ToolStrip_SEPARATORTHICKNESS, ToolStrip_SEPARATORTHICKNESS); } } - /// + /// protected internal override Padding DefaultMargin { get { return Padding.Empty; @@ -410,24 +410,24 @@ public override Size GetPreferredSize(Size constrainingSize) { parent = Owner; } if (parent == null) { - return new Size(WINBAR_SEPARATORTHICKNESS, WINBAR_SEPARATORTHICKNESS); + return new Size(ToolStrip_SEPARATORTHICKNESS, ToolStrip_SEPARATORTHICKNESS); } ToolStripDropDownMenu dropDownMenu = parent as ToolStripDropDownMenu; if (dropDownMenu != null) { - return new Size(parent.Width - (parent.Padding.Horizontal - dropDownMenu.ImageMargin.Width), WINBAR_SEPARATORTHICKNESS); + return new Size(parent.Width - (parent.Padding.Horizontal - dropDownMenu.ImageMargin.Width), ToolStrip_SEPARATORTHICKNESS); } else { if (parent.LayoutStyle != ToolStripLayoutStyle.HorizontalStackWithOverflow || parent.LayoutStyle != ToolStripLayoutStyle.VerticalStackWithOverflow) { // we dont actually know what size to make it, so just keep it a stock size. - constrainingSize.Width = WINBAR_SEPARATORHEIGHT; - constrainingSize.Height = WINBAR_SEPARATORHEIGHT; + constrainingSize.Width = ToolStrip_SEPARATORHEIGHT; + constrainingSize.Height = ToolStrip_SEPARATORHEIGHT; } if (IsVertical) { - return new Size(WINBAR_SEPARATORTHICKNESS, constrainingSize.Height); + return new Size(ToolStrip_SEPARATORTHICKNESS, constrainingSize.Height); } else { - return new Size(constrainingSize.Width, WINBAR_SEPARATORTHICKNESS); + return new Size(constrainingSize.Width, ToolStrip_SEPARATORTHICKNESS); } } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSeparatorRenderEventArgs.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSeparatorRenderEventArgs.cs index 96aec8f80f3..fe78b055b98 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSeparatorRenderEventArgs.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSeparatorRenderEventArgs.cs @@ -7,12 +7,12 @@ namespace System.Windows.Forms { /// - /// This class represents all the information to render the winbar + /// This class represents all the information to render the ToolStrip /// public class ToolStripSeparatorRenderEventArgs : ToolStripItemRenderEventArgs { /// - /// This class represents all the information to render the winbar + /// This class represents all the information to render the ToolStrip /// public ToolStripSeparatorRenderEventArgs(Graphics g, ToolStripSeparator separator, bool vertical) : base(g, separator) { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSeparatorRenderEventHandler.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSeparatorRenderEventHandler.cs index 2cea2693b1e..7ba0161f3aa 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSeparatorRenderEventHandler.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSeparatorRenderEventHandler.cs @@ -5,7 +5,7 @@ namespace System.Windows.Forms { /// - /// Called when the background of a winbar item is being rendered + /// Called when the background of a ToolStrip item is being rendered /// public delegate void ToolStripSeparatorRenderEventHandler(object sender, ToolStripSeparatorRenderEventArgs e); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSplitButton.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSplitButton.cs index f029bd6d632..2cddeffdaa8 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSplitButton.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSplitButton.cs @@ -405,7 +405,7 @@ protected override AccessibleObject CreateAccessibilityInstance() { } protected override ToolStripDropDown CreateDefaultDropDown() { - // AutoGenerate a Winbar DropDown - set the property so we hook events + // AutoGenerate a ToolStrip DropDown - set the property so we hook events return new ToolStripDropDownMenu(this, /*isAutoGenerated=*/true); } @@ -645,7 +645,7 @@ internal virtual bool ShouldSerializeDropDownButtonWidth() { /// This class represents the item to the left of the dropdown [ A |v] (e.g the "A") /// It exists so that we can use our existing methods for text and image layout /// and have a place to stick certain state information like pushed and selected - /// Note since this is NOT an actual item hosted on the Winbar - it wont get things + /// Note since this is NOT an actual item hosted on the ToolStrip - it wont get things /// like MouseOver, wont be laid out by the ToolStrip, etc etc. This is purely internal /// convenience. /// diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSplitStackLayout.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSplitStackLayout.cs index 914b29bedec..347eb3fb343 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSplitStackLayout.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSplitStackLayout.cs @@ -81,7 +81,7 @@ private bool OverflowRequired { } } - // the current winbar we're operating over. + // the current ToolStrip we're operating over. public ToolStrip ToolStrip { get { return toolStrip; @@ -89,7 +89,7 @@ public ToolStrip ToolStrip { } // - // This method will mark whether items should be placed in the overflow or on the main winbar. + // This method will mark whether items should be placed in the overflow or on the main ToolStrip. // private void CalculatePlacementsHorizontal() { ResetItemPlacements(); @@ -139,7 +139,7 @@ private void CalculatePlacementsHorizontal() { } // - // This method will mark whether items should be placed in the overflow or on the main winbar. + // This method will mark whether items should be placed in the overflow or on the main ToolStrip. // private void CalculatePlacementsVertical() { ResetItemPlacements(); @@ -577,7 +577,7 @@ private void ResetItemPlacements() { // // This method is called when we are walking through the item collection and we have realized that we - // need to free up "X" amount of space to be able to fit an item onto the winbar. + // need to free up "X" amount of space to be able to fit an item onto the ToolStrip. private int SendNextItemToOverflow(int spaceNeeded, bool horizontal) { #if DEBUG if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "SendNextItemToOverflow attempting to free {0}", spaceNeeded)); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripStatusLabel.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripStatusLabel.cs index 0f4d04213db..5b365cf55e0 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripStatusLabel.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripStatusLabel.cs @@ -19,7 +19,7 @@ namespace System.Windows.Forms { /// /// - /// A non selectable winbar item + /// A non selectable ToolStrip item /// [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.StatusStrip)] public class ToolStripStatusLabel : ToolStripLabel, IAutomationLiveRegion { @@ -35,7 +35,7 @@ public class ToolStripStatusLabel : ToolStripLabel, IAutomationLiveRegion { /// /// - /// A non selectable winbar item + /// A non selectable ToolStrip item /// public ToolStripStatusLabel() { Initialize(); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSystemRenderer.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSystemRenderer.cs index fd02dc0411b..b56a2716b15 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSystemRenderer.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSystemRenderer.cs @@ -88,25 +88,25 @@ private static bool GetPen(Color color, ref Pen pen) { } } /// - /// translates the winbar item state into a toolbar state, which is something the renderer understands + /// translates the ToolStrip item state into a toolbar state, which is something the renderer understands /// private static int GetItemState(ToolStripItem item) { return (int)GetToolBarState(item); } /// - /// translates the winbar item state into a toolbar state, which is something the renderer understands + /// translates the ToolStrip item state into a toolbar state, which is something the renderer understands /// private static int GetSplitButtonDropDownItemState(ToolStripSplitButton item) { return (int)GetSplitButtonToolBarState(item, true); } /// - /// translates the winbar item state into a toolbar state, which is something the renderer understands + /// translates the ToolStrip item state into a toolbar state, which is something the renderer understands /// private static int GetSplitButtonItemState(ToolStripSplitButton item) { return (int)GetSplitButtonToolBarState(item, false); } /// - /// translates the winbar item state into a toolbar state, which is something the renderer understands + /// translates the ToolStrip item state into a toolbar state, which is something the renderer understands /// private static ToolBarState GetSplitButtonToolBarState(ToolStripSplitButton button, bool dropDownButton) { ToolBarState state = ToolBarState.Normal; @@ -136,7 +136,7 @@ private static ToolBarState GetSplitButtonToolBarState(ToolStripSplitButton butt } /// - /// translates the winbar item state into a toolbar state, which is something the renderer understands + /// translates the ToolStrip item state into a toolbar state, which is something the renderer understands /// private static ToolBarState GetToolBarState(ToolStripItem item) { ToolBarState state = ToolBarState.Normal; @@ -166,7 +166,7 @@ private static ToolBarState GetToolBarState(ToolStripItem item) { /// /// - /// Draw the winbar background. ToolStrip users should override this if they want to draw differently. + /// Draw the ToolStrip background. ToolStrip users should override this if they want to draw differently. /// protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) { ToolStrip toolStrip = e.ToolStrip; @@ -436,7 +436,7 @@ protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e) { } - /// + /// protected override void OnRenderToolStripStatusLabelBackground(ToolStripItemRenderEventArgs e) { RenderLabelInternal(e); ToolStripStatusLabel item = e.Item as ToolStripStatusLabel; diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripTextBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripTextBox.cs index 29cac21eb3a..2646c29e8f6 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripTextBox.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripTextBox.cs @@ -186,33 +186,33 @@ private void HandleReadOnlyChanged(object sender, EventArgs e) { private void HandleTextBoxTextAlignChanged(object sender, EventArgs e) { RaiseEvent(EventTextBoxTextAlignChanged, e); } - /// + /// protected virtual void OnAcceptsTabChanged(EventArgs e) { RaiseEvent(EventAcceptsTabChanged, e); } - /// + /// protected virtual void OnBorderStyleChanged(EventArgs e) { RaiseEvent(EventBorderStyleChanged, e); } - /// + /// protected virtual void OnHideSelectionChanged(EventArgs e) { RaiseEvent(EventHideSelectionChanged, e); } - /// + /// protected virtual void OnModifiedChanged(EventArgs e) { RaiseEvent(EventModifiedChanged, e); } - /// + /// protected virtual void OnMultilineChanged(EventArgs e) { RaiseEvent(EventMultilineChanged, e); } - /// + /// protected virtual void OnReadOnlyChanged(EventArgs e) { RaiseEvent(EventReadOnlyChanged, e); } - /// + /// protected override void OnSubscribeControlEvents(Control control) { TextBox textBox = control as TextBox; if (textBox != null) { @@ -231,7 +231,7 @@ protected override void OnSubscribeControlEvents(Control control) { base.OnSubscribeControlEvents(control); } - /// + /// protected override void OnUnsubscribeControlEvents(Control control) { TextBox textBox = control as TextBox; @@ -257,7 +257,7 @@ internal override bool ShouldSerializeFont() { #region WrappedProperties - /// + /// [ SRCategory(nameof(SR.CatBehavior)), DefaultValue(false), @@ -268,7 +268,7 @@ public bool AcceptsTab { set { TextBox.AcceptsTab = value; } } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), DefaultValue(false), @@ -279,7 +279,7 @@ public bool AcceptsReturn { set { TextBox.AcceptsReturn = value; } } - /// + /// [ DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Localizable(true), @@ -292,7 +292,7 @@ public System.Windows.Forms.AutoCompleteStringCollection AutoCompleteCustomSourc set { TextBox.AutoCompleteCustomSource = value; } } - /// + /// [ DefaultValue(AutoCompleteMode.None), SRDescription(nameof(SR.TextBoxAutoCompleteModeDescr)), @@ -303,7 +303,7 @@ public AutoCompleteMode AutoCompleteMode { set { TextBox.AutoCompleteMode = value; } } - /// + /// [ DefaultValue(AutoCompleteSource.None), SRDescription(nameof(SR.TextBoxAutoCompleteSourceDescr)), @@ -314,7 +314,7 @@ public AutoCompleteSource AutoCompleteSource { set { TextBox.AutoCompleteSource = value; } } - /// + /// [ SRCategory(nameof(SR.CatAppearance)), DefaultValue(BorderStyle.Fixed3D), @@ -326,7 +326,7 @@ public BorderStyle BorderStyle { set { TextBox.BorderStyle = value; } } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), Browsable(false), @@ -337,7 +337,7 @@ public bool CanUndo { get { return TextBox.CanUndo; } } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), DefaultValue(CharacterCasing.Normal), @@ -348,7 +348,7 @@ public CharacterCasing CharacterCasing { set { TextBox.CharacterCasing = value; } } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), DefaultValue(true), @@ -359,7 +359,7 @@ public bool HideSelection { set { TextBox.HideSelection = value; } } - /// + /// [ SRCategory(nameof(SR.CatAppearance)), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), @@ -372,7 +372,7 @@ public string[] Lines { set { TextBox.Lines = value; } } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), DefaultValue(32767), @@ -384,7 +384,7 @@ public int MaxLength { set { TextBox.MaxLength = value; } } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), Browsable(false), @@ -396,7 +396,7 @@ public bool Modified { set { TextBox.Modified = value; } } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), DefaultValue(false), @@ -410,7 +410,7 @@ public bool Multiline { set { TextBox.Multiline = value; } } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), DefaultValue(false), @@ -420,7 +420,7 @@ public bool ReadOnly { get { return TextBox.ReadOnly; } set { TextBox.ReadOnly = value; } } - /// + /// [ SRCategory(nameof(SR.CatAppearance)), Browsable(false), @@ -432,7 +432,7 @@ public string SelectedText { set { TextBox.SelectedText = value; } } - /// + /// [ SRCategory(nameof(SR.CatAppearance)), Browsable(false), @@ -443,7 +443,7 @@ public int SelectionLength { get { return TextBox.SelectionLength; } set { TextBox.SelectionLength = value; } } - /// + /// [ SRCategory(nameof(SR.CatAppearance)), Browsable(false), @@ -454,7 +454,7 @@ public int SelectionStart { get { return TextBox.SelectionStart; } set { TextBox.SelectionStart = value; } } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), DefaultValue(true), @@ -464,7 +464,7 @@ public bool ShortcutsEnabled { get { return TextBox.ShortcutsEnabled; } set { TextBox.ShortcutsEnabled = value; } } - /// + /// [Browsable(false)] public int TextLength { get { return TextBox.TextLength; } @@ -481,7 +481,7 @@ public HorizontalAlignment TextBoxTextAlign { set { TextBox.TextAlign = value; } } - /// + /// [ SRCategory(nameof(SR.CatBehavior)), Localizable(true), @@ -500,7 +500,7 @@ public bool WordWrap { #region WrappedEvents - /// + /// [SRCategory(nameof(SR.CatPropertyChanged)), SRDescription(nameof(SR.TextBoxBaseOnAcceptsTabChangedDescr))] public event EventHandler AcceptsTabChanged { add { @@ -512,7 +512,7 @@ public event EventHandler AcceptsTabChanged { } - /// + /// [SRCategory(nameof(SR.CatPropertyChanged)), SRDescription(nameof(SR.TextBoxBaseOnBorderStyleChangedDescr))] public event EventHandler BorderStyleChanged { add { @@ -523,7 +523,7 @@ public event EventHandler BorderStyleChanged { } } - /// + /// [SRCategory(nameof(SR.CatPropertyChanged)), SRDescription(nameof(SR.TextBoxBaseOnHideSelectionChangedDescr))] public event EventHandler HideSelectionChanged { add { @@ -534,7 +534,7 @@ public event EventHandler HideSelectionChanged { } } - /// + /// [SRCategory(nameof(SR.CatPropertyChanged)), SRDescription(nameof(SR.TextBoxBaseOnModifiedChangedDescr))] public event EventHandler ModifiedChanged { add { @@ -545,7 +545,7 @@ public event EventHandler ModifiedChanged { } } - /// + /// [SRCategory(nameof(SR.CatPropertyChanged)), SRDescription(nameof(SR.TextBoxBaseOnMultilineChangedDescr)),Browsable(false),EditorBrowsable(EditorBrowsableState.Never)] public event EventHandler MultilineChanged { add { @@ -556,7 +556,7 @@ public event EventHandler MultilineChanged { } } - /// + /// [SRCategory(nameof(SR.CatPropertyChanged)), SRDescription(nameof(SR.TextBoxBaseOnReadOnlyChangedDescr))] public event EventHandler ReadOnlyChanged { add { @@ -568,7 +568,7 @@ public event EventHandler ReadOnlyChanged { } - /// + /// [SRCategory(nameof(SR.CatPropertyChanged)), SRDescription(nameof(SR.ToolStripTextBoxTextBoxTextAlignChangedDescr))] public event EventHandler TextBoxTextAlignChanged { add { @@ -581,39 +581,39 @@ public event EventHandler TextBoxTextAlignChanged { #endregion WrappedEvents #region WrappedMethods - /// + /// public void AppendText(string text) { TextBox.AppendText(text); } - /// + /// public void Clear(){ TextBox.Clear(); } - /// + /// public void ClearUndo() {TextBox.ClearUndo(); } - /// + /// public void Copy() {TextBox.Copy(); } - /// + /// public void Cut() {TextBox.Copy(); } - /// + /// public void DeselectAll() { TextBox.DeselectAll(); } - /// + /// public char GetCharFromPosition(System.Drawing.Point pt) { return TextBox.GetCharFromPosition(pt); } - /// + /// public int GetCharIndexFromPosition(System.Drawing.Point pt) { return TextBox.GetCharIndexFromPosition(pt); } - /// + /// public int GetFirstCharIndexFromLine(int lineNumber) { return TextBox.GetFirstCharIndexFromLine(lineNumber); } - /// + /// public int GetFirstCharIndexOfCurrentLine() { return TextBox.GetFirstCharIndexOfCurrentLine(); } - /// + /// public int GetLineFromCharIndex(int index) { return TextBox.GetLineFromCharIndex(index); } - /// + /// public System.Drawing.Point GetPositionFromCharIndex(int index) { return TextBox.GetPositionFromCharIndex(index); } - /// + /// public void Paste() { TextBox.Paste(); } - /// + /// public void ScrollToCaret() { TextBox.ScrollToCaret(); } - /// + /// public void Select(int start, int length) { TextBox.Select(start, length); } - /// + /// public void SelectAll() { TextBox.SelectAll(); } - /// + /// public void Undo() { TextBox.Undo(); } #endregion private class ToolStripTextBoxControl : TextBox { From 2169937c956e40768220bf0b97c633a70e0fa012 Mon Sep 17 00:00:00 2001 From: Judit Varsanyi Rozsa Date: Mon, 8 Apr 2019 13:20:17 -0700 Subject: [PATCH 06/10] update tests for control designer --- .../tests/UnitTests/ControlDesignerTests.cs | 190 ++++++++++++------ 1 file changed, 129 insertions(+), 61 deletions(-) diff --git a/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs b/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs index e4fa0febbd4..cba3d02b550 100644 --- a/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs +++ b/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs @@ -20,151 +20,159 @@ public void AccessibleObjectField() [Fact] public void BehaviorServiceProperty() { - Assert.Throws(() => controlDesigner.GetBehaviorServiceProperty()); + Assert.NotNull(controlDesigner.GetBehaviorServiceProperty()); } [Fact] public void AssociatedComponentsProperty() { - Assert.Throws(() => controlDesigner.AssociatedComponents); + Assert.NotNull(controlDesigner.AssociatedComponents); } [Fact] public void AccessibilityObjectField() { - Assert.Throws(() => controlDesigner.AccessibilityObject); + Assert.NotNull(controlDesigner.AccessibilityObject); } [Fact] public void ControlProperty() { - Assert.Throws(() => controlDesigner.Control); + Assert.NotNull(controlDesigner.Control); } [Fact] public void EnableDragRectProperty() { - Assert.Throws(() => controlDesigner.GetEnableDragRectProperty()); + Assert.False(controlDesigner.GetEnableDragRectProperty()); } [Fact] public void ParentComponentProperty() { - Assert.Throws(() => controlDesigner.GetParentComponentProperty()); + Assert.NotNull(controlDesigner.GetParentComponentProperty()); } [Fact] public void ParticipatesWithSnapLinesProperty() { - Assert.Throws(() => controlDesigner.ParticipatesWithSnapLines); + Assert.True(controlDesigner.ParticipatesWithSnapLines); } [Fact] public void AutoResizeHandlesProperty() { - Assert.Throws(() => controlDesigner.AutoResizeHandles = true); - Assert.Throws(() => controlDesigner.AutoResizeHandles); + Assert.True(controlDesigner.AutoResizeHandles = true); + Assert.True(controlDesigner.AutoResizeHandles); } [Fact] public void SelectionRulesProperty() { - Assert.Throws(() => controlDesigner.SelectionRules); - } - - [Fact] - public void SnapLinesProperty() - { - Assert.Throws(() => controlDesigner.SnapLines); + Assert.Equal(SelectionRules.Visible ,controlDesigner.SelectionRules); } [Fact] public void InheritanceAttributeProperty() { - Assert.Throws(() => controlDesigner.GetInheritanceAttributeProperty()); + Assert.NotNull(controlDesigner.GetInheritanceAttributeProperty()); } [Fact] public void NumberOfInternalControlDesignersTest() { - Assert.Throws(() => controlDesigner.NumberOfInternalControlDesigners()); + Assert.Equal(0, controlDesigner.NumberOfInternalControlDesigners()); } [Fact] public void InternalControlDesignerTest() { - Assert.Throws(() => controlDesigner.InternalControlDesigner(1)); + Assert.NotNull(controlDesigner.InternalControlDesigner(1)); } [Fact] public void BaseWndProcTest() { Message m = default; - Assert.Throws(() => controlDesigner.BaseWndProcMethod(ref m)); + try + { + controlDesigner.BaseWndProcMethod(ref m); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } [Fact] public void CanBeParentedToTest() { - Assert.Throws(() => controlDesigner.CanBeParentedTo(new ParentControlDesigner())); + Assert.NotNull(controlDesigner); + controlDesigner.Initialize(new Button()); + Assert.True(controlDesigner.CanBeParentedTo(new ParentControlDesigner())); } [Fact] public void DefWndProcTest() { Message m = default; - Assert.Throws(() => controlDesigner.DefWndProcMethod(ref m)); + try + { + controlDesigner.DefWndProcMethod(ref m); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } [Fact] public void DisplayErrorTest() { - Assert.Throws(() => controlDesigner.DisplayErrorMethod(new Exception())); + try + { + controlDesigner.DisplayErrorMethod(new Exception()); + controlDesigner.InitializeExistingComponent(null); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } - - /// - /// Bool data - /// - public static TheoryData BoolData => CommonTestHelper.GetBoolTheoryData(); - [Theory] - [MemberData(nameof(BoolData))] - public void DisposeTest(bool val) + [Fact] + public void DisposeTest() { - Assert.Throws(() => controlDesigner.DisposeMethod(val)); + try + { + controlDesigner.DisposeMethod(true); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } [Fact] public void EnableDesignModeTest() { - Assert.Throws(() => controlDesigner.EnableDesignModeMethod(null, "fake")); + Assert.True(controlDesigner.EnableDesignModeMethod(null, "fake")); } + public static TheoryData BoolData => CommonTestHelper.GetBoolTheoryData(); + [Theory] [MemberData(nameof(BoolData))] public void EnableDragDropTest(bool val) { - Assert.Throws(() => controlDesigner.EnableDragDropMethod(val)); - } - - /// - /// Data for the GlyphSelectionType enum - /// - public static TheoryData GlyphSelectionTypeData => - CommonTestHelper.GetEnumTheoryData(); - - [Theory] - [MemberData(nameof(GlyphSelectionTypeData))] - public void GetControlGlyphTest(Behavior.GlyphSelectionType glyphSelectionType) - { - Assert.Throws(() => controlDesigner.GetControlGlyphMethod(glyphSelectionType)); - } - - [Theory] - [MemberData(nameof(GlyphSelectionTypeData))] - public void GetGlyphsTest(Behavior.GlyphSelectionType glyphSelectionType) - { - Assert.Throws(() => controlDesigner.GetGlyphs(glyphSelectionType)); + try + { + controlDesigner.EnableDragDropMethod(val); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } [Fact] @@ -176,45 +184,105 @@ public void GetHitTest() [Fact] public void HookChildControlsTest() { - Assert.Throws(() => controlDesigner.HookChildControlsMethod(null)); + Assert.NotNull(controlDesigner); + controlDesigner.Initialize(new Button()); + try + { + + controlDesigner.HookChildControlsMethod(new Control()); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } [Fact] public void InitializeTest() { - Assert.Throws(() => controlDesigner.Initialize(null)); + try + { + controlDesigner.Initialize(null); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } [Fact] public void InitializeExistingComponentTest() { - Assert.Throws(() => controlDesigner.InitializeExistingComponent(null)); + Assert.NotNull(controlDesigner); + controlDesigner.Initialize(new Button()); + try + { + controlDesigner.InitializeExistingComponent(null); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } [Fact] public void InitializeNewComponentTest() { - Assert.Throws(() => controlDesigner.InitializeNewComponent(null)); + Assert.NotNull(controlDesigner); + controlDesigner.Initialize(new Button()); + try + { + controlDesigner.InitializeNewComponent(null); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } [Fact] public void OnSetComponentDefaultsTest() { #pragma warning disable 618 - Assert.Throws(() => controlDesigner.OnSetComponentDefaults()); + Assert.NotNull(controlDesigner); + controlDesigner.Initialize(new Button()); + try + { + controlDesigner.OnSetComponentDefaults(); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } #pragma warning restore 618 } [Fact] public void OnContextMenuTest() { - Assert.Null(Record.Exception(() => controlDesigner.OnContextMenuMethod(0, 0))); + try + { + controlDesigner.OnContextMenuMethod(0, 0); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } [Fact] public void OnCreateHandleTest() { - Assert.Throws(() => controlDesigner.OnCreateHandleMethod()); + Assert.NotNull(controlDesigner); + controlDesigner.Initialize(new Button()); + try + { + controlDesigner.OnCreateHandleMethod(); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } } } From 808aefa2df77024fbc8edb04dde1e977ac7bc00b Mon Sep 17 00:00:00 2001 From: Judit Varsanyi Rozsa Date: Mon, 8 Apr 2019 15:36:45 -0700 Subject: [PATCH 07/10] update tests --- .../tests/UnitTests/ControlDesignerTests.cs | 113 ++++-------------- 1 file changed, 23 insertions(+), 90 deletions(-) diff --git a/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs b/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs index cba3d02b550..626961668db 100644 --- a/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs +++ b/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs @@ -9,59 +9,45 @@ namespace System.Windows.Forms.Design.Tests { public class ControlDesignerTests { - internal MockControlDesigner controlDesigner = new MockControlDesigner(); - [Fact] public void AccessibleObjectField() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Assert.Null(controlDesigner.GetAccessibleObjectField()); } [Fact] public void BehaviorServiceProperty() { - Assert.NotNull(controlDesigner.GetBehaviorServiceProperty()); - } - - [Fact] - public void AssociatedComponentsProperty() - { - Assert.NotNull(controlDesigner.AssociatedComponents); + MockControlDesigner controlDesigner = new MockControlDesigner(); + Assert.Null(controlDesigner.GetBehaviorServiceProperty()); } [Fact] public void AccessibilityObjectField() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Assert.NotNull(controlDesigner.AccessibilityObject); } - [Fact] - public void ControlProperty() - { - Assert.NotNull(controlDesigner.Control); - } - [Fact] public void EnableDragRectProperty() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Assert.False(controlDesigner.GetEnableDragRectProperty()); } - - [Fact] - public void ParentComponentProperty() - { - Assert.NotNull(controlDesigner.GetParentComponentProperty()); - } [Fact] public void ParticipatesWithSnapLinesProperty() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Assert.True(controlDesigner.ParticipatesWithSnapLines); } [Fact] public void AutoResizeHandlesProperty() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Assert.True(controlDesigner.AutoResizeHandles = true); Assert.True(controlDesigner.AutoResizeHandles); } @@ -69,30 +55,30 @@ public void AutoResizeHandlesProperty() [Fact] public void SelectionRulesProperty() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Assert.Equal(SelectionRules.Visible ,controlDesigner.SelectionRules); } [Fact] public void InheritanceAttributeProperty() { + MockControlDesigner controlDesigner = new MockControlDesigner(); + Assert.NotNull(controlDesigner); + controlDesigner.Initialize(new Button()); Assert.NotNull(controlDesigner.GetInheritanceAttributeProperty()); } [Fact] public void NumberOfInternalControlDesignersTest() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Assert.Equal(0, controlDesigner.NumberOfInternalControlDesigners()); } - [Fact] - public void InternalControlDesignerTest() - { - Assert.NotNull(controlDesigner.InternalControlDesigner(1)); - } - [Fact] public void BaseWndProcTest() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Message m = default; try { @@ -107,64 +93,19 @@ public void BaseWndProcTest() [Fact] public void CanBeParentedToTest() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Assert.NotNull(controlDesigner); controlDesigner.Initialize(new Button()); Assert.True(controlDesigner.CanBeParentedTo(new ParentControlDesigner())); } - [Fact] - public void DefWndProcTest() - { - Message m = default; - try - { - controlDesigner.DefWndProcMethod(ref m); - } - catch (Exception ex) - { - Assert.True(false, "Expected no exception, but got: " + ex.Message); - } - } - - [Fact] - public void DisplayErrorTest() - { - try - { - controlDesigner.DisplayErrorMethod(new Exception()); - controlDesigner.InitializeExistingComponent(null); - } - catch (Exception ex) - { - Assert.True(false, "Expected no exception, but got: " + ex.Message); - } - } - - [Fact] - public void DisposeTest() - { - try - { - controlDesigner.DisposeMethod(true); - } - catch (Exception ex) - { - Assert.True(false, "Expected no exception, but got: " + ex.Message); - } - } - - [Fact] - public void EnableDesignModeTest() - { - Assert.True(controlDesigner.EnableDesignModeMethod(null, "fake")); - } - public static TheoryData BoolData => CommonTestHelper.GetBoolTheoryData(); [Theory] [MemberData(nameof(BoolData))] public void EnableDragDropTest(bool val) { + MockControlDesigner controlDesigner = new MockControlDesigner(); try { controlDesigner.EnableDragDropMethod(val); @@ -178,12 +119,14 @@ public void EnableDragDropTest(bool val) [Fact] public void GetHitTest() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Assert.False(controlDesigner.GetHitTestMethod(new Drawing.Point())); } [Fact] public void HookChildControlsTest() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Assert.NotNull(controlDesigner); controlDesigner.Initialize(new Button()); try @@ -200,24 +143,10 @@ public void HookChildControlsTest() [Fact] public void InitializeTest() { + MockControlDesigner controlDesigner = new MockControlDesigner(); try { - controlDesigner.Initialize(null); - } - catch (Exception ex) - { - Assert.True(false, "Expected no exception, but got: " + ex.Message); - } - } - - [Fact] - public void InitializeExistingComponentTest() - { - Assert.NotNull(controlDesigner); - controlDesigner.Initialize(new Button()); - try - { - controlDesigner.InitializeExistingComponent(null); + controlDesigner.Initialize(new Button()); } catch (Exception ex) { @@ -228,6 +157,7 @@ public void InitializeExistingComponentTest() [Fact] public void InitializeNewComponentTest() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Assert.NotNull(controlDesigner); controlDesigner.Initialize(new Button()); try @@ -243,6 +173,7 @@ public void InitializeNewComponentTest() [Fact] public void OnSetComponentDefaultsTest() { + MockControlDesigner controlDesigner = new MockControlDesigner(); #pragma warning disable 618 Assert.NotNull(controlDesigner); controlDesigner.Initialize(new Button()); @@ -260,6 +191,7 @@ public void OnSetComponentDefaultsTest() [Fact] public void OnContextMenuTest() { + MockControlDesigner controlDesigner = new MockControlDesigner(); try { controlDesigner.OnContextMenuMethod(0, 0); @@ -273,6 +205,7 @@ public void OnContextMenuTest() [Fact] public void OnCreateHandleTest() { + MockControlDesigner controlDesigner = new MockControlDesigner(); Assert.NotNull(controlDesigner); controlDesigner.Initialize(new Button()); try From 82844d4dc7d7530ea5a6dbee89fc22826ee6b3f4 Mon Sep 17 00:00:00 2001 From: Judit Varsanyi Rozsa Date: Mon, 8 Apr 2019 15:37:36 -0700 Subject: [PATCH 08/10] remove bitmap selector --- src/Common/src/BitmapSelector.cs | 217 ------------------ .../src/System.Windows.Forms.Design.csproj | 1 - .../StandardCommandToolStripMenuItem.cs | 2 +- .../ToolStripItemCustomMenuItemCollection.cs | 4 +- .../src/System.Windows.Forms.csproj | 1 - .../Forms/DataGridViewRowHeaderCell.cs | 2 +- .../PropertyGridInternal/PropertyGridView.cs | 2 +- 7 files changed, 5 insertions(+), 224 deletions(-) delete mode 100644 src/Common/src/BitmapSelector.cs diff --git a/src/Common/src/BitmapSelector.cs b/src/Common/src/BitmapSelector.cs deleted file mode 100644 index db468642757..00000000000 --- a/src/Common/src/BitmapSelector.cs +++ /dev/null @@ -1,217 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Drawing { - using System; - using System.Configuration; - using System.Drawing.Configuration; - using System.IO; - using System.Reflection; - - /// - /// Provides methods to select from multiple bitmaps depending on a "bitmapSuffix" config setting. - /// - internal static class BitmapSelector { - - /// - /// Gets the bitmap ID suffix defined in the application configuration, or string.Empty if - /// the suffix is not specified. Internal for unit tests - /// - /// - /// For performance, the suffix is cached in a static variable so it only has to be read - /// once per appdomain. - /// - private static string _suffix; - internal static string Suffix { - get { - if (_suffix == null) { - _suffix = string.Empty; - var section = ConfigurationManager.GetSection("system.drawing") as SystemDrawingSection; - if (section != null) { - var value = section.BitmapSuffix; - if (value != null && value is string) { - _suffix = (string)value; - } - } - } - return _suffix; - } - set { - // So unit tests can clear the cached suffix - _suffix = value; - } - } - - /// - /// Appends the current suffix to . The suffix is appended - /// before the existing extension (if any). Internal for unit tests. - /// - /// - /// The new path with the suffix included. If there is no suffix defined or there are - /// invalid characters in the original path, the original path is returned. - /// - internal static string AppendSuffix(string filePath) { - try { - return Path.ChangeExtension(filePath, Suffix + Path.GetExtension(filePath)); - } - catch (ArgumentException) { // there are invalid characters in the path - return filePath; - } - } - - /// - /// Returns with the current suffix appended (before the - /// existing extension) if the resulting file path exists; otherwise the original path is - /// returned. - /// - public static string GetFileName(string originalPath) { - if (Suffix == string.Empty) - return originalPath; - - string newPath = AppendSuffix(originalPath); - return File.Exists(newPath) ? newPath : originalPath; - } - - // Calls assembly.GetManifestResourceStream in a try/catch and returns null if not found - private static Stream GetResourceStreamHelper(Assembly assembly, Type type, string name) { - Stream stream = null; - try { - stream = assembly.GetManifestResourceStream(type, name); - } - catch (FileNotFoundException) { - } - return stream; - } - - private static bool DoesAssemblyHaveCustomAttribute(Assembly assembly, string typeName) { - return DoesAssemblyHaveCustomAttribute(assembly, assembly.GetType(typeName)); - } - - private static bool DoesAssemblyHaveCustomAttribute(Assembly assembly, Type attrType) { - if (attrType != null) { - var attr = assembly.GetCustomAttributes(attrType, false); - if (attr.Length > 0) { - return true; - } - } - return false; - } - - // internal for unit tests - internal static bool SatelliteAssemblyOptIn(Assembly assembly) { - // Try 4.5 public attribute type first - if (DoesAssemblyHaveCustomAttribute(assembly, typeof(BitmapSuffixInSatelliteAssemblyAttribute))) { - return true; - } - - // Also load attribute type by name for dlls compiled against older frameworks - return DoesAssemblyHaveCustomAttribute(assembly, "System.Drawing.BitmapSuffixInSatelliteAssemblyAttribute"); - } - - // internal for unit tests - internal static bool SameAssemblyOptIn(Assembly assembly) { - // Try 4.5 public attribute type first - if (DoesAssemblyHaveCustomAttribute(assembly, typeof(BitmapSuffixInSameAssemblyAttribute))) { - return true; - } - - // Also load attribute type by name for dlls compiled against older frameworks - return DoesAssemblyHaveCustomAttribute(assembly, "System.Drawing.BitmapSuffixInSameAssemblyAttribute"); - } - - /// - /// Returns a resource stream loaded from the appropriate location according to the current - /// suffix. - /// - /// The assembly from which the stream is loaded - /// The type whose namespace is used to scope the manifest resource name - /// The name of the manifest resource being requested - /// - /// The manifest resource stream corresponding to with the - /// current suffix applied; or if that is not found, the stream corresponding to . - /// - public static Stream GetResourceStream(Assembly assembly, Type type, string originalName) { - if (Suffix != string.Empty) { - try { - // Resource with suffix has highest priority - if (SameAssemblyOptIn(assembly)) { - string newName = AppendSuffix(originalName); - Stream stream = GetResourceStreamHelper(assembly, type, newName); - if (stream != null) { - return stream; - } - } - } - catch { - // Ignore failures and continue to try other options - } - - try { - // Satellite assembly has second priority, using the original name - if (SatelliteAssemblyOptIn(assembly)) { - AssemblyName assemblyName = assembly.GetName(); - assemblyName.Name += Suffix; - assemblyName.ProcessorArchitecture = ProcessorArchitecture.None; - Assembly satellite = Assembly.Load(assemblyName); - if (satellite != null) { - Stream stream = GetResourceStreamHelper(satellite, type, originalName); - if (stream != null) { - return stream; - } - } - } - } - catch { - // Ignore failures and continue to try other options - } - } - - // Otherwise fall back to specified assembly and original name requested - return assembly.GetManifestResourceStream(type, originalName); - } - - /// - /// Returns a resource stream loaded from the appropriate location according to the current - /// suffix. - /// - /// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name - /// The name of the manifest resource being requested - /// - /// The manifest resource stream corresponding to with the - /// current suffix applied; or if that is not found, the stream corresponding to . - /// - public static Stream GetResourceStream(Type type, string originalName) { - return GetResourceStream(type.Module.Assembly, type, originalName); - } - - /// - /// Returns an Icon created from a resource stream loaded from the appropriate location according to the current - /// suffix. - /// - /// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name - /// The name of the manifest resource being requested - /// - /// The icon created from a manifest resource stream corresponding to with the - /// current suffix applied; or if that is not found, the stream corresponding to . - /// - public static Icon CreateIcon(Type type, string originalName) { - return new Icon(GetResourceStream(type, originalName)); - } - - /// - /// Returns an Bitmap created from a resource stream loaded from the appropriate location according to the current - /// suffix. - /// - /// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name - /// The name of the manifest resource being requested - /// - /// The bitmap created from a manifest resource stream corresponding to with the - /// current suffix applied; or if that is not found, the stream corresponding to . - /// - public static Bitmap CreateBitmap(Type type, string originalName) { - return new Bitmap(GetResourceStream(type, originalName)); - } - - } -} diff --git a/src/System.Windows.Forms.Design/src/System.Windows.Forms.Design.csproj b/src/System.Windows.Forms.Design/src/System.Windows.Forms.Design.csproj index ffcdf249915..dfc3fc009a4 100644 --- a/src/System.Windows.Forms.Design/src/System.Windows.Forms.Design.csproj +++ b/src/System.Windows.Forms.Design/src/System.Windows.Forms.Design.csproj @@ -31,7 +31,6 @@ - diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardCommandToolStripMenuItem.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardCommandToolStripMenuItem.cs index f45ad62ee8b..614d2ce5a32 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardCommandToolStripMenuItem.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/StandardCommandToolStripMenuItem.cs @@ -84,7 +84,7 @@ public override Image Image { if (_name != null) { - _image = new Bitmap(BitmapSelector.GetResourceStream(typeof(ToolStripMenuItem), _name + ".bmp")); + _image = new Icon(typeof(ToolStripMenuItem), _name + ".bmp").ToBitmap(); } ImageTransparentColor = Color.Magenta; } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs index 61f6321639e..3157e67ff23 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs @@ -74,7 +74,7 @@ private ToolStripMenuItem CreatePropertyBasedItem(string text, string propertyNa { if (!string.IsNullOrEmpty(imageName)) { - item.Image = new Bitmap(BitmapSelector.GetResourceStream(typeof(ToolStripMenuItem), imageName)); + item.Image = new Icon(typeof(ToolStripMenuItem), imageName).ToBitmap(); item.ImageTransparentColor = Color.Magenta; } @@ -206,7 +206,7 @@ private void PopulateList() editItemsToolStripMenuItem = new ToolStripMenuItem(); editItemsToolStripMenuItem.Text = SR.ToolStripDropDownItemCollectionEditorVerb; editItemsToolStripMenuItem.Click += new EventHandler(OnEditItemsMenuItemClick); - editItemsToolStripMenuItem.Image = new Bitmap(BitmapSelector.GetResourceStream(typeof(ToolStripMenuItem), "editdropdownlist.bmp")); + editItemsToolStripMenuItem.Image = new Icon(typeof(ToolStripMenuItem), "editdropdownlist.bmp").ToBitmap(); editItemsToolStripMenuItem.ImageTransparentColor = Color.Magenta; this.Add(editItemsToolStripMenuItem); } diff --git a/src/System.Windows.Forms/src/System.Windows.Forms.csproj b/src/System.Windows.Forms/src/System.Windows.Forms.csproj index a6aa76ecfa5..a84db31c254 100644 --- a/src/System.Windows.Forms/src/System.Windows.Forms.csproj +++ b/src/System.Windows.Forms/src/System.Windows.Forms.csproj @@ -29,7 +29,6 @@ - diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/DataGridViewRowHeaderCell.cs b/src/System.Windows.Forms/src/System/Windows/Forms/DataGridViewRowHeaderCell.cs index 6a67125689f..6e188cadf5d 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/DataGridViewRowHeaderCell.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/DataGridViewRowHeaderCell.cs @@ -190,7 +190,7 @@ private static Bitmap GetArrowStarBitmap(bool rightToLeft) private static Bitmap GetBitmapFromIcon(string iconName) { Size desiredSize = new Size(iconsWidth, iconsHeight); - Icon icon = new Icon(BitmapSelector.GetResourceStream(typeof(DataGridViewRowHeaderCell), iconName), desiredSize); + Icon icon = new Icon(new Icon(typeof(DataGridViewHeaderCell), iconName), desiredSize); Bitmap b = icon.ToBitmap(); icon.Dispose(); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/PropertyGridInternal/PropertyGridView.cs b/src/System.Windows.Forms/src/System/Windows/Forms/PropertyGridInternal/PropertyGridView.cs index 479060e5915..9777b4832fe 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/PropertyGridInternal/PropertyGridView.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/PropertyGridInternal/PropertyGridView.cs @@ -319,7 +319,7 @@ private Button DialogButton { private static Bitmap GetBitmapFromIcon(string iconName, int iconsWidth, int iconsHeight) { Size desiredSize = new Size(iconsWidth, iconsHeight); - Icon icon = new Icon(BitmapSelector.GetResourceStream(typeof(PropertyGrid), iconName), desiredSize); + Icon icon = new Icon(new Icon(typeof(PropertyGrid), iconName), desiredSize); Bitmap b = icon.ToBitmap(); icon.Dispose(); From 50a5e8f57e3bd043487805523fe1d6aa174ecb90 Mon Sep 17 00:00:00 2001 From: Judit Varsanyi Rozsa Date: Tue, 9 Apr 2019 15:23:03 -0700 Subject: [PATCH 09/10] replace \n\r occurences in resx files to new line --- .../src/Resources/SR.Designer.cs | 14 +++++++--- .../src/Resources/SR.resx | 14 +++++++--- .../src/Resources/xlf/SR.cs.xlf | 26 ++++++++++++++----- .../src/Resources/xlf/SR.de.xlf | 26 ++++++++++++++----- .../src/Resources/xlf/SR.es.xlf | 26 ++++++++++++++----- .../src/Resources/xlf/SR.fr.xlf | 26 ++++++++++++++----- .../src/Resources/xlf/SR.it.xlf | 26 ++++++++++++++----- .../src/Resources/xlf/SR.ja.xlf | 26 ++++++++++++++----- .../src/Resources/xlf/SR.ko.xlf | 26 ++++++++++++++----- .../src/Resources/xlf/SR.pl.xlf | 26 ++++++++++++++----- .../src/Resources/xlf/SR.pt-BR.xlf | 26 ++++++++++++++----- .../src/Resources/xlf/SR.ru.xlf | 26 ++++++++++++++----- .../src/Resources/xlf/SR.tr.xlf | 26 ++++++++++++++----- .../src/Resources/xlf/SR.zh-Hans.xlf | 26 ++++++++++++++----- .../src/Resources/xlf/SR.zh-Hant.xlf | 26 ++++++++++++++----- 15 files changed, 282 insertions(+), 84 deletions(-) diff --git a/src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs b/src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs index e96278210cb..0d6ba386d81 100644 --- a/src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs +++ b/src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs @@ -205,7 +205,8 @@ public static string CommandSetDelete { } /// - /// Looks up a localized string similar to An error occurred while processing this command.\r\n{0}. + /// Looks up a localized string similar to An error occurred while processing this command. + /// {0}. /// public static string CommandSetError { get { @@ -412,7 +413,12 @@ public static string ContextMenuViewCode { } /// - /// Looks up a localized string similar to The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2}. + /// Looks up a localized string similar to The control {0} has thrown an unhandled exception in the designer and has been disabled. + /// + /// Exception: + /// {1} + /// + /// Stack trace:{2}. /// public static string ControlDesigner_WndProcException { get { @@ -817,7 +823,9 @@ public static string DesignSurfaceDesignerNotLoaded { } /// - /// Looks up a localized string similar to An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0}. + /// Looks up a localized string similar to An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + /// + /// {0}. /// public static string DesignSurfaceFatalError { get { diff --git a/src/System.Windows.Forms.Design/src/Resources/SR.resx b/src/System.Windows.Forms.Design/src/Resources/SR.resx index a1937f1a1d7..c8f3eb4fd7f 100644 --- a/src/System.Windows.Forms.Design/src/Resources/SR.resx +++ b/src/System.Windows.Forms.Design/src/Resources/SR.resx @@ -229,7 +229,8 @@ Delete {0} components - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} Format {0} components (spacing) @@ -395,7 +396,9 @@ Cannot create a view for this design surface because the designer is not loaded. - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} The designer loader did not provide a root component but has not indicated why. @@ -422,7 +425,12 @@ Undock in Parent Container - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} The Locked property determines if we can move or resize the control. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf index e75f75b7056..a42c7d226a0 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - Při načítání dokumentu došlo k chybě. Opravte chybu a pak zkuste dokument načíst znovu. Následuje chybová zpráva:\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + Při načítání dokumentu došlo k chybě. Opravte chybu a pak zkuste dokument načíst znovu. Následuje chybová zpráva:\r\n\r\n{0} diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf index aa425ac0607..5920fb6715b 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - Fehler beim Laden des Dokuments. Beheben Sie den Fehler, und wiederholen Sie den Vorgang. Fehlermeldung:\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + Fehler beim Laden des Dokuments. Beheben Sie den Fehler, und wiederholen Sie den Vorgang. Fehlermeldung:\r\n\r\n{0} diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf index 88be7f1479a..73b159be393 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - Error al cargar el documento. Corrija el error y, a continuación, intente volver a cargar el documento. Mensaje de error:\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + Error al cargar el documento. Corrija el error y, a continuación, intente volver a cargar el documento. Mensaje de error:\r\n\r\n{0} diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf index 030108b9bc3..6e6f1df0e39 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - Une erreur s'est produite durant le chargement du document. Corrigez l'erreur, puis essayez de recharger le document. Message d'erreur :\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + Une erreur s'est produite durant le chargement du document. Corrigez l'erreur, puis essayez de recharger le document. Message d'erreur :\r\n\r\n{0} diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf index 1c23a80c6ae..43728dcb3ca 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - Si è verificato un errore durante il caricamento del documento. Correggere l'errore e provare nuovamente a caricare il documento. Messaggio di errore:\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + Si è verificato un errore durante il caricamento del documento. Correggere l'errore e provare nuovamente a caricare il documento. Messaggio di errore:\r\n\r\n{0} diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf index 251bd2d4129..8a6473a46ba 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - ドキュメントの読み込み中にエラーが発生しました。このエラーを修正してから、もう一度ドキュメントを読み込んでください。エラー メッセージは以下のとおりです。\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + ドキュメントの読み込み中にエラーが発生しました。このエラーを修正してから、もう一度ドキュメントを読み込んでください。エラー メッセージは以下のとおりです。\r\n\r\n{0} diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf index 313643b20fd..b03dc899bdc 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - 문서를 로드하는 동안 오류가 발생했습니다. 오류를 수정한 다음 문서를 다시 로드해 보세요. 다음 오류 메시지가 나타납니다.\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + 문서를 로드하는 동안 오류가 발생했습니다. 오류를 수정한 다음 문서를 다시 로드해 보세요. 다음 오류 메시지가 나타납니다.\r\n\r\n{0} diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf index 9e907227b41..e32dd042eda 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - Wystąpił błąd podczas ładowania dokumentu. Usuń błąd, a następnie spróbuj załadować ponownie dokument. Oto treść komunikatu o błędzie:\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + Wystąpił błąd podczas ładowania dokumentu. Usuń błąd, a następnie spróbuj załadować ponownie dokument. Oto treść komunikatu o błędzie:\r\n\r\n{0} diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf index 38714826c4c..22cb792ad3a 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - Erro ao carregar o documento. Corrija o erro e tente carregar o documento novamente. Mensagem de erro:\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + Erro ao carregar o documento. Corrija o erro e tente carregar o documento novamente. Mensagem de erro:\r\n\r\n{0} diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf index 057f5e32f0c..877e87b6914 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - Произошла ошибка при загрузке документа. Исправьте ошибку и повторите загрузку. Сообщение об ошибке:\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + Произошла ошибка при загрузке документа. Исправьте ошибку и повторите загрузку. Сообщение об ошибке:\r\n\r\n{0} diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf index 8e5aadd4dee..94ed618f685 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - Belge yüklenirken hata oluştu. Hatayı düzeltin ve belgeyi yüklemeyi yeniden deneyin. Hata iletisi:\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + Belge yüklenirken hata oluştu. Hatayı düzeltin ve belgeyi yüklemeyi yeniden deneyin. Hata iletisi:\r\n\r\n{0} diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf index 61fbfa44508..f2fbb5b2a37 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - 加载文档时出错。请修复此错误,然后再尝试重新加载该文档。错误消息如下所示:\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + 加载文档时出错。请修复此错误,然后再尝试重新加载该文档。错误消息如下所示:\r\n\r\n{0} diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf index b0b13159bc0..3b3d2e09d8e 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf @@ -68,8 +68,10 @@ - An error occurred while processing this command.\r\n{0} - An error occurred while processing this command.\r\n{0} + An error occurred while processing this command. + {0} + An error occurred while processing this command. + {0} @@ -198,8 +200,18 @@ - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} - The control {0} has thrown an unhandled exception in the designer and has been disabled. \r\n\r\nException:\r\n{1}\r\n\r\nStack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} + The control {0} has thrown an unhandled exception in the designer and has been disabled. + + Exception: + {1} + + Stack trace:{2} @@ -263,8 +275,10 @@ - An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows:\r\n\r\n{0} - 載入文件時發生錯誤。請修正錯誤,然後重新嘗試載入文件。錯誤訊息為:\r\n\r\n{0} + An error occurred while loading the document. Fix the error, and then try loading the document again. The error message follows: + + {0} + 載入文件時發生錯誤。請修正錯誤,然後重新嘗試載入文件。錯誤訊息為:\r\n\r\n{0} From 807980801dabbe47d4fd81089711eccb21234664 Mon Sep 17 00:00:00 2001 From: Judit Varsanyi Rozsa Date: Wed, 10 Apr 2019 16:39:04 -0700 Subject: [PATCH 10/10] rename ControlDesignerMock to TestControlDesigner --- .../tests/UnitTests/ControlDesignerTests.cs | 38 +++++++++---------- ...trolDesigner.cs => TestControlDesigner.cs} | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) rename src/System.Windows.Forms.Design/tests/UnitTests/{Mocks/MockControlDesigner.cs => TestControlDesigner.cs} (97%) diff --git a/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs b/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs index 626961668db..51568c546ab 100644 --- a/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs +++ b/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs @@ -12,42 +12,42 @@ public class ControlDesignerTests [Fact] public void AccessibleObjectField() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.Null(controlDesigner.GetAccessibleObjectField()); } [Fact] public void BehaviorServiceProperty() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.Null(controlDesigner.GetBehaviorServiceProperty()); } [Fact] public void AccessibilityObjectField() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.NotNull(controlDesigner.AccessibilityObject); } [Fact] public void EnableDragRectProperty() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.False(controlDesigner.GetEnableDragRectProperty()); } [Fact] public void ParticipatesWithSnapLinesProperty() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.True(controlDesigner.ParticipatesWithSnapLines); } [Fact] public void AutoResizeHandlesProperty() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.True(controlDesigner.AutoResizeHandles = true); Assert.True(controlDesigner.AutoResizeHandles); } @@ -55,14 +55,14 @@ public void AutoResizeHandlesProperty() [Fact] public void SelectionRulesProperty() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.Equal(SelectionRules.Visible ,controlDesigner.SelectionRules); } [Fact] public void InheritanceAttributeProperty() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.NotNull(controlDesigner); controlDesigner.Initialize(new Button()); Assert.NotNull(controlDesigner.GetInheritanceAttributeProperty()); @@ -71,14 +71,14 @@ public void InheritanceAttributeProperty() [Fact] public void NumberOfInternalControlDesignersTest() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.Equal(0, controlDesigner.NumberOfInternalControlDesigners()); } [Fact] public void BaseWndProcTest() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Message m = default; try { @@ -93,7 +93,7 @@ public void BaseWndProcTest() [Fact] public void CanBeParentedToTest() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.NotNull(controlDesigner); controlDesigner.Initialize(new Button()); Assert.True(controlDesigner.CanBeParentedTo(new ParentControlDesigner())); @@ -105,7 +105,7 @@ public void CanBeParentedToTest() [MemberData(nameof(BoolData))] public void EnableDragDropTest(bool val) { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); try { controlDesigner.EnableDragDropMethod(val); @@ -119,14 +119,14 @@ public void EnableDragDropTest(bool val) [Fact] public void GetHitTest() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.False(controlDesigner.GetHitTestMethod(new Drawing.Point())); } [Fact] public void HookChildControlsTest() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.NotNull(controlDesigner); controlDesigner.Initialize(new Button()); try @@ -143,7 +143,7 @@ public void HookChildControlsTest() [Fact] public void InitializeTest() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); try { controlDesigner.Initialize(new Button()); @@ -157,7 +157,7 @@ public void InitializeTest() [Fact] public void InitializeNewComponentTest() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.NotNull(controlDesigner); controlDesigner.Initialize(new Button()); try @@ -173,7 +173,7 @@ public void InitializeNewComponentTest() [Fact] public void OnSetComponentDefaultsTest() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); #pragma warning disable 618 Assert.NotNull(controlDesigner); controlDesigner.Initialize(new Button()); @@ -191,7 +191,7 @@ public void OnSetComponentDefaultsTest() [Fact] public void OnContextMenuTest() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); try { controlDesigner.OnContextMenuMethod(0, 0); @@ -205,7 +205,7 @@ public void OnContextMenuTest() [Fact] public void OnCreateHandleTest() { - MockControlDesigner controlDesigner = new MockControlDesigner(); + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.NotNull(controlDesigner); controlDesigner.Initialize(new Button()); try diff --git a/src/System.Windows.Forms.Design/tests/UnitTests/Mocks/MockControlDesigner.cs b/src/System.Windows.Forms.Design/tests/UnitTests/TestControlDesigner.cs similarity index 97% rename from src/System.Windows.Forms.Design/tests/UnitTests/Mocks/MockControlDesigner.cs rename to src/System.Windows.Forms.Design/tests/UnitTests/TestControlDesigner.cs index b39961fa395..cbb2d7bb8b4 100644 --- a/src/System.Windows.Forms.Design/tests/UnitTests/Mocks/MockControlDesigner.cs +++ b/src/System.Windows.Forms.Design/tests/UnitTests/TestControlDesigner.cs @@ -8,7 +8,7 @@ namespace System.Windows.Forms.Design.Tests { - internal class MockControlDesigner : ControlDesigner + internal class TestControlDesigner : ControlDesigner { internal AccessibleObject GetAccessibleObjectField() {