diff --git a/src/Common/src/CompModSwitches.cs b/src/Common/src/CompModSwitches.cs index bf57603f201..f22eaddb3d7 100644 --- a/src/Common/src/CompModSwitches.cs +++ b/src/Common/src/CompModSwitches.cs @@ -39,8 +39,9 @@ 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 { @@ -294,7 +295,7 @@ public static TraceSwitch SetBounds { return setBounds; } } - #endif +#endif public static TraceSwitch HandleLeak { get { @@ -305,16 +306,16 @@ public static TraceSwitch 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; } } - internal static BooleanSwitch CommonDesignerServices + + public static BooleanSwitch CommonDesignerServices { get { diff --git a/src/Common/src/NativeMethods.cs b/src/Common/src/NativeMethods.cs index 44ece3695ab..01467f31169 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, @@ -5701,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; @@ -6042,6 +6045,10 @@ public UiaRect(System.Drawing.Rectangle r) { [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 = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern short GetKeyState(int keyCode); @@ -6065,6 +6072,8 @@ public static bool DeleteObject(IntPtr hObject) [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)] + [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); diff --git a/src/Common/src/SafeNativeMethods.cs b/src/Common/src/SafeNativeMethods.cs index ca23a08463a..bbe978c066e 100644 --- a/src/Common/src/SafeNativeMethods.cs +++ b/src/Common/src/SafeNativeMethods.cs @@ -476,6 +476,11 @@ public static IntPtr CopyImageAsCursor(HandleRef hImage, int uType, int cxDesire 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)] @@ -573,11 +578,12 @@ public static IntPtr CreateRectRgn(int x1, int y1, int x2, int y2) { public static extern bool ShowWindow(HandleRef hWnd, int nCmdShow); [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); + public static extern bool SetWindowPos(HandleRef hWnd, HandleRef hWndInsertAfter, + int x, int y, int cx, int cy, int flags); [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int flags); + 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)] @@ -598,8 +604,8 @@ public static bool TrackMouseEvent(NativeMethods.TRACKMOUSEEVENT tme) { public static extern bool RedrawWindow(HandleRef hwnd, NativeMethods.COMRECT rcUpdate, HandleRef hrgnUpdate, int flags); [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] 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); @@ -699,7 +705,6 @@ public static extern bool BitBlt(HandleRef hDC, int x, int y, int nWidth, int nH HandleRef hSrcDC, int xSrc, int ySrc, int dwRop); [DllImport(ExternDll.Gdi32, ExactSpelling = true, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] 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)] @@ -864,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; @@ -910,18 +921,6 @@ public interface IFontDisp { short Charset {get;set;} } - - [DllImport(ExternDll.Gdi32, ExactSpelling = true, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - public static extern bool RoundRect(HandleRef hDC, int left, int top, int right, int bottom, int width, int height); - - [DllImport(ExternDll.Gdi32, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - public static extern bool GetTextMetrics(HandleRef hdc, NativeMethods.TEXTMETRIC tm); - - [ResourceExposure(ResourceScope.None)] - [DllImport(ExternDll.Uxtheme, CharSet = CharSet.Auto)] - public extern static int SetWindowTheme(IntPtr hWnd, string subAppName, string subIdList); } } diff --git a/src/Common/src/UnsafeNativeMethods.cs b/src/Common/src/UnsafeNativeMethods.cs index 86ecfb73baa..a3ae53791eb 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); @@ -198,20 +201,6 @@ internal static bool IsVista public static extern int CoGetMalloc(int dwReserved, out IMalloc pMalloc); - /* Commenting this out until someone actually needs to use it again... - [DllImport(ExternDll.Ole32)] - public static extern int OleSetMenuDescriptor(IntPtr hOleMenu, IntPtr hWndFrame, IntPtr hWndActiveObject, IOleInPlaceFrame frame, IOleInPlaceActiveObject activeObject); - */ - - /* Commenting this out until someone actually needs to use it again... - [DllImport(ExternDll.Kernel32)] - public static extern bool IsBadReadPtr(HandleRef ptr, int size); - */ - - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - public static extern int GetMessageTime(); - [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] [ResourceExposure(ResourceScope.Process)] public static extern int GetWindowThreadProcessId(HandleRef hWnd, out int lpdwProcessId); @@ -836,7 +825,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); @@ -1001,7 +995,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)] @@ -1016,17 +1014,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); @@ -1041,10 +1029,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); @@ -1054,6 +1039,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. /// @@ -1143,6 +1131,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); @@ -1169,6 +1161,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); @@ -1193,7 +1190,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); @@ -1202,6 +1203,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.Designer.cs b/src/System.Windows.Forms.Design/src/Resources/SR.Designer.cs index 9dc46dd2d42..0d6ba386d81 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}.. /// @@ -160,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 { @@ -222,6 +268,164 @@ 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. + /// + /// Exception: + /// {1} + /// + /// Stack 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}'.. /// @@ -232,7 +436,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 { @@ -582,6 +786,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.. /// @@ -601,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 { @@ -717,6 +941,33 @@ public static string InheritanceServiceReadOnlyCollection { } } + /// + /// Looks up a localized string similar to '{1}' is not a valid value for '{0}'.. + /// + 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.. /// @@ -745,65 +996,785 @@ public static string RTL { } /// - /// Looks up a localized string similar to Argument should be a non-empty string.. + /// 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 ToolboxItemInvalidKey { + public static string SerializationManagerAreadyInSession { get { - return ResourceManager.GetString("ToolboxItemInvalidKey", resourceCulture); + return ResourceManager.GetString("SerializationManagerAreadyInSession", resourceCulture); } } /// - /// Looks up a localized string similar to Property {0} requires an argument of type {1}.. + /// Looks up a localized string similar to Duplicate declaration of member '{0}'. /// - public static string ToolboxItemInvalidPropertyType { + public static string SerializationManagerDuplicateComponentDecl { get { - return ResourceManager.GetString("ToolboxItemInvalidPropertyType", resourceCulture); + return ResourceManager.GetString("SerializationManagerDuplicateComponentDecl", resourceCulture); } } /// - /// Looks up a localized string similar to Toolbox item cannot be modified.. + /// Looks up a localized string similar to The name '{0}' is already used by another object.. /// - public static string ToolboxItemLocked { + public static string SerializationManagerNameInUse { get { - return ResourceManager.GetString("ToolboxItemLocked", resourceCulture); + return ResourceManager.GetString("SerializationManagerNameInUse", resourceCulture); } } /// - /// Looks up a localized string similar to Data type {0} is not serializable. Items added to a property dictionary must be serializable.. + /// Looks up a localized string similar to Type '{0}' does not have a constructor with parameters of types {1}.. /// - public static string ToolboxItemValueNotSerializable { + public static string SerializationManagerNoMatchingCtor { get { - return ResourceManager.GetString("ToolboxItemValueNotSerializable", resourceCulture); + return ResourceManager.GetString("SerializationManagerNoMatchingCtor", resourceCulture); } } /// - /// Looks up a localized string similar to Auto Arrange Tray Icons. + /// 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 TrayAutoArrange { + public static string SerializationManagerNoSession { get { - return ResourceManager.GetString("TrayAutoArrange", resourceCulture); + return ResourceManager.GetString("SerializationManagerNoSession", resourceCulture); } } /// - /// Looks up a localized string similar to Line Up Tray Icons. + /// Looks up a localized string similar to Cannot name the object '{0}' because it is already named '{1}'.. /// - public static string TrayLineUpIcons { + public static string SerializationManagerObjectHasName { get { - return ResourceManager.GetString("TrayLineUpIcons", resourceCulture); + return ResourceManager.GetString("SerializationManagerObjectHasName", resourceCulture); } } /// - /// Looks up a localized string similar to Show Large or Small Icons. + /// Looks up a localized string similar to This method cannot be invoked because the serialization manager has an active serialization session.. /// - public static string TrayShowLargeIcons { + public static string SerializationManagerWithinSession { get { - return ResourceManager.GetString("TrayShowLargeIcons", resourceCulture); + return ResourceManager.GetString("SerializationManagerWithinSession", 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 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 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 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 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 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 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 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 &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); } } diff --git a/src/System.Windows.Forms.Design/src/Resources/SR.resx b/src/System.Windows.Forms.Design/src/Resources/SR.resx index 9c2ff7e65d1..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. @@ -409,15 +412,50 @@ 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}. + + Resize {0} + + + Resize {0} Controls + + + Dock in Parent Container + + + Undock in Parent Container + + + 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. + + + '{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}'. @@ -427,23 +465,182 @@ 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. + + &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. - - You cannot create a new session because this serialization manager already has an active serialization session. + + ShowCheckMargin + + + Toggles the ShowCheckMargin property + + + ShowImageMargin + + + Toggles the ShowImageMargin property Duplicate declaration of member '{0}' @@ -451,29 +648,29 @@ 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. + + Elements of type {0} are not supported. The serializer expects the element to be one of the following: {1}. - - Code statements for the object '{0}' were lost during serialization. This may have been a result of another object misbehaving during serialization. + + Error reading resources from the resource file for the culture {0}: {1} - - The object '{0}' failed to serialize itself. It may not support code generation. + + Error reading resources from the resource file for the default culture: {0} - - Array rank '{0}' is too high. Visual Studio can only save and load arrays with a rank of 1. + + Adding Item - - Members of type '{0}' cannot be serialized. + + &Edit DropDownItems... + + + Con&vert To + + + The serialization store is closed. New objects cannot be added to a closed store. Complete deserialization of {0} failed. @@ -481,7 +678,64 @@ This type of serialization store is not supported. Use a store returned by the CreateStore method. - - The serialization store is closed. New objects cannot be added to a closed store. + + 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 93d5039bdd9..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 @@ -22,6 +22,16 @@ 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. Úložiště serializace je zavřeno. Do zavřeného úložiště nelze přidávat nové objekty. @@ -37,26 +47,6 @@ Tento typ úložiště serializací není podporován. Použijte úložiště vrácené metodou CreateStore. - - Adding event '{0}' - Přidání události {0} - - - - Unhandled VT: {0}. - Nezpracovaná hodnota VT: {0}. - - - - Double cannot be converted to a date. - Nelze převést typ Double na typ Date. - - - - Integer cannot be converted to a float. - Nelze převést typ Integer na typ Float. - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + Přidání události {0} + + + + Unhandled VT: {0}. + Nezpracovaná hodnota VT: {0}. + + + + Double cannot be converted to a date. + Nelze převést typ Double na typ Date. + + + + Integer cannot be converted to a float. + Nelze převést typ Integer na typ 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,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 Komponenta .NET @@ -362,6 +481,16 @@ 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. Zprostředkovatel rozšířeného objektu {0} již byl přidán jako rozšiřující objekt. V případě přidání dalšího by došlo k duplicitě vlastností. @@ -372,14 +501,14 @@ Jen pro čtení - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ Proměnná {0} není deklarována nebo jí dosud nebyla přiřazena hodnota. + + &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 musí být neprázdný řetězec. @@ -522,11 +941,31 @@ Typ dat {0} není serializovatelný. Položky přidané do slovníku vlastností musí být serializovatelné. + + 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. Typ {0} není v cílové architektuře k dispozici. + + Error + Error + + Add Component Přidat součást @@ -572,26 +1011,6 @@ Služba {0} je požadována, ale nebyla nalezena. Pokud jste tuto službu odebrali, zajistěte její náhradu. - - 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 - - - - Error - Error - - Not implemented. Není implementováno. @@ -612,6 +1031,11 @@ 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 8ae18c8bcae..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 @@ -22,6 +22,16 @@ 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. Der Serialisierungsspeicher ist geschlossen. Neue Objekte können nicht zu einem geschlossenen Speicher hinzugefügt werden. @@ -37,26 +47,6 @@ Dieser Typ von Serialisierungsspeicher wird nicht unterstützt. Verwenden Sie einen Speicher, der von der CreateStore-Methode zurückgegeben wird. - - Adding event '{0}' - Das Ereignis {0} wird hinzugefügt. - - - - Unhandled VT: {0}. - Unbehandelter VT: {0}. - - - - Double cannot be converted to a date. - "Double" kann nicht in "Date" konvertiert werden. - - - - Integer cannot be converted to a float. - "Integer" kann nicht in "Float" konvertiert werden. - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + Das Ereignis {0} wird hinzugefügt. + + + + Unhandled VT: {0}. + Unbehandelter VT: {0}. + + + + Double cannot be converted to a date. + "Double" kann nicht in "Date" konvertiert werden. + + + + Integer cannot be converted to a float. + "Integer" kann nicht in "Float" konvertiert werden. + + + + 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,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-Komponente @@ -362,6 +481,16 @@ 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. Der Extenderanbieter {0} wurde bereits als Extender hinzugefügt. Wenn ein weiterer hinzugefügt wird, führt dies zu doppelten Eigenschaften. @@ -372,14 +501,14 @@ Schreibgeschützt - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ Die Variable {0} wurde nicht deklariert oder nicht zugeordnet. + + &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. Das Argument muss eine nicht leere Zeichenfolge sein. @@ -522,11 +941,31 @@ Der Datentyp {0} kann nicht serialisiert werden. Elemente, die einem Eigenschaftswörterbuch hinzugefügt werden, müssen serialisierbar sein. + + 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. Der Typ "{0}" ist im Zielframework nicht vorhanden. + + Error + Error + + Add Component Komponente hinzufügen @@ -572,26 +1011,6 @@ Der Dienst {0} ist erforderlich, wurde aber nicht gefunden. Wenn Sie den Dienst entfernt haben, stellen Sie sicher, dass ein Ersatz verfügbar ist. - - 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 - - - - Error - Error - - Not implemented. Nicht implementiert. @@ -612,6 +1031,11 @@ 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 d3fed26bd14..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 @@ -22,6 +22,16 @@ 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. El almacén de serialización está cerrado. No se pueden agregar nuevos objetos a un almacén cerrado. @@ -37,26 +47,6 @@ No se admite este tipo de almacén de serialización. Utilice un almacén devuelto por el método CreateStore. - - Adding event '{0}' - Agregando un evento '{0}' - - - - Unhandled VT: {0}. - VT no controlado: {0}. - - - - Double cannot be converted to a date. - No se puede convertir double en date. - - - - Integer cannot be converted to a float. - No se puede convertir integer en float. - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + Agregando un evento '{0}' + + + + Unhandled VT: {0}. + VT no controlado: {0}. + + + + Double cannot be converted to a date. + No se puede convertir double en date. + + + + Integer cannot be converted to a float. + No se puede convertir integer en 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,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 Componente .NET @@ -362,6 +481,16 @@ 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. El proveedor extensor {0} ya se ha agregado como extensor. Si agrega otro habría propiedades duplicadas. @@ -372,14 +501,14 @@ Solo lectura - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ La variable '{0}' no está declarada o no se asignó nunca. + + &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. El argumento no debe ser una cadena vacía. @@ -522,11 +941,31 @@ El tipo de datos {0} no se puede serializar. Los elementos agregados a un diccionario de propiedades deben ser serializables. + + 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. El tipo "{0}" no está disponible en la plataforma de destino. + + Error + Error + + Add Component Agregar componente @@ -572,26 +1011,6 @@ El servicio {0} es obligatorio pero no se pudo encontrar. Si ha quitado este servicio, asegúrese de proporcionar un reemplazo. - - 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 - - - - Error - Error - - Not implemented. Sin implementar. @@ -612,6 +1031,11 @@ 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 105acea6b10..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 @@ -22,6 +22,16 @@ 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. Le magasin de sérialisation est fermé. Impossible d'ajouter de nouveaux objets à un magasin fermé. @@ -37,26 +47,6 @@ Ce type de magasin de sérialisation n'est pas pris en charge. Utilisez un magasin retourné par la méthode CreateStore. - - Adding event '{0}' - Ajout de l'événement '{0}' - - - - Unhandled VT: {0}. - VT non géré : {0}. - - - - Double cannot be converted to a date. - Impossible de convertir une valeur double en date. - - - - Integer cannot be converted to a float. - Impossible de convertir une valeur integer en float. - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + Ajout de l'événement '{0}' + + + + Unhandled VT: {0}. + VT non géré : {0}. + + + + Double cannot be converted to a date. + Impossible de convertir une valeur double en date. + + + + Integer cannot be converted to a float. + Impossible de convertir une valeur integer en 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,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 Composant .NET @@ -362,6 +481,16 @@ 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. Le fournisseur d'extendeurs {0} a déjà été ajouté en tant qu'extendeur. L'ajout d'un autre créerait des propriétés en double. @@ -372,14 +501,14 @@ En lecture seule - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ La variable '{0}' n'est pas déclarée ou n'a jamais été assignée. + + &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. L'argument ne peut pas être une chaîne vide. @@ -522,11 +941,31 @@ Le type de données {0} n'est pas sérialisable. Les éléments ajoutés à un dictionnaire de propriétés doivent être sérialisables. + + 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. Le type '{0}' n'est pas disponible dans le Framework cible. + + Error + Error + + Add Component Ajouter un composant @@ -572,26 +1011,6 @@ Le service {0} requis est introuvable. Si vous avez supprimé ce service, assurez-vous qu'un service de remplacement est fourni. - - 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 - - - - Error - Error - - Not implemented. Non implémenté. @@ -612,6 +1031,11 @@ 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 5cfcb411e63..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 @@ -22,6 +22,16 @@ 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. L'archivio di serializzazione è chiuso. Non è possibile aggiungere nuovi oggetti a un archivio chiuso. @@ -37,26 +47,6 @@ Questo tipo di archivio di serializzazione non è supportato. Usare un archivio restituito dal metodo CreateStore. - - Adding event '{0}' - Aggiunta evento '{0}' - - - - Unhandled VT: {0}. - VT non gestito: {0}. - - - - Double cannot be converted to a date. - Impossibile convertire un tipo double in un tipo date. - - - - Integer cannot be converted to a float. - Impossibile convertire un numero intero in un valore float. - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + Aggiunta evento '{0}' + + + + Unhandled VT: {0}. + VT non gestito: {0}. + + + + Double cannot be converted to a date. + Impossibile convertire un tipo double in un tipo date. + + + + Integer cannot be converted to a float. + Impossibile convertire un numero intero in un valore 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,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 Componente .NET @@ -362,6 +481,16 @@ 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. Il provider di Extender {0} è già stato aggiunto come Extender. Aggiungendone un altro si creerebbero proprietà duplicate. @@ -372,14 +501,14 @@ Sola lettura - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ La variabile '{0}' non è stata dichiarata o non è mai stata assegnata. + + &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. L'argomento deve essere una stringa non vuota. @@ -522,11 +941,31 @@ Tipo di dati {0} non serializzabile. Gli elementi aggiunti a un dizionario di proprietà devono essere serializzabili. + + 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. Il tipo '{0}' non è disponibile nel framework di destinazione. + + Error + Error + + Add Component Aggiungi componente @@ -572,26 +1011,6 @@ Impossibile trovare il servizio richiesto {0}. Se tale servizio è stato rimosso, sarà necessario sostituirlo. - - 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 - - - - Error - Error - - Not implemented. Non implementato. @@ -612,6 +1031,11 @@ 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 3180c9ac759..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 @@ -22,6 +22,16 @@ 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. シリアル化ストアは閉じられています。 閉じられたストアに新しいオブジェクトを追加することはできません。 @@ -37,26 +47,6 @@ シリアル化ストアのこの型はサポートされていません。 CreateStore メソッドによって返されたストアを使用してください。 - - Adding event '{0}' - イベント '{0}' の追加 - - - - Unhandled VT: {0}. - ハンドルされていない VT: {0} - - - - Double cannot be converted to a date. - double を date に変換できません。 - - - - Integer cannot be converted to a float. - integer を float に変換できません。 - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + イベント '{0}' の追加 + + + + Unhandled VT: {0}. + ハンドルされていない VT: {0} + + + + Double cannot be converted to a date. + double を date に変換できません。 + + + + Integer cannot be converted to a float. + integer を 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,16 @@ デザイナーでスナップ線を使用するかどうかを制御します。 true の場合、スナップ線がガイドとして使用されます。 false の場合、グリッド線が使用されます。 + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET コンポーネント @@ -362,6 +481,16 @@ この 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. 拡張プロバイダー {0} はエクステンダーとして既に追加されています。 もう 1 つ追加すると、プロパティが重複します。 @@ -372,14 +501,14 @@ 読み取り専用 - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ 変数 '{0}' は宣言されていないか、または割り当てられていません。 + + &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. 引数には空ではない文字列を指定してください。 @@ -522,11 +941,31 @@ データ型 {0} はシリアル化可能ではありません。プロパティ辞書に追加される項目はシリアル化可能でなければなりません。 + + 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. 型 '{0}' は、ターゲット フレームワークでは使用できません。 + + Error + Error + + Add Component コンポーネントの追加 @@ -572,26 +1011,6 @@ サービス {0} が必要ですが、見つかりませんでした。 このサービスを削除した場合、必ず置換したサービスを指定してください。 - - 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 - - - - Error - Error - - Not implemented. 実装されていません。 @@ -612,6 +1031,11 @@ 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 5b5bef8bde6..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 @@ -22,6 +22,16 @@ 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. Serialization 저장소가 닫혀 있습니다. 닫힌 저장소에는 새 개체를 추가할 수 없습니다. @@ -37,26 +47,6 @@ 이러한 형식의 serialization 저장소는 지원되지 않습니다. CreateStore 메서드에서 반환된 저장소를 사용하십시오. - - Adding event '{0}' - '{0}' 이벤트를 추가하고 있습니다. - - - - Unhandled VT: {0}. - 처리되지 않은 VT입니다. {0} - - - - Double cannot be converted to a date. - double 형식을 date 형식으로 변환할 수 없습니다. - - - - Integer cannot be converted to a float. - integer 형식을 float 형식으로 변환할 수 없습니다. - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + '{0}' 이벤트를 추가하고 있습니다. + + + + Unhandled VT: {0}. + 처리되지 않은 VT입니다. {0} + + + + Double cannot be converted to a date. + double 형식을 date 형식으로 변환할 수 없습니다. + + + + Integer cannot be converted to a float. + integer 형식을 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,16 @@ 디자이너에서 맞춤선을 사용할지 여부를 제어합니다. true이면 맞춤선이 안내선으로 사용되고 false이면 모눈선이 사용됩니다. + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET 구성 요소 @@ -362,6 +481,16 @@ 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. Extender 공급자 {0}이(가) Extender로 이미 추가되었습니다. 다른 공급자를 추가하면 속성이 중복됩니다. @@ -372,14 +501,14 @@ 읽기 전용 - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ '{0}' 변수를 선언하거나 할당하지 않았습니다. + + &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. 인수는 비어 있지 않은 문자열이어야 합니다. @@ -522,11 +941,31 @@ 데이터 형식 {0}을(를) serialize할 수 없습니다. 속성 사전에 추가된 항목은 serialize할 수 있어야 합니다. + + 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. 대상 프레임워크에서 '{0}' 형식을 사용할 수 없습니다. + + Error + Error + + Add Component 구성 요소 추가 @@ -572,26 +1011,6 @@ 필요한 {0} 서비스를 찾을 수 없습니다. 이 서비스를 제거한 경우에는 대체할 서비스를 제공해야 합니다. - - 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 - - - - Error - Error - - Not implemented. 구현되지 않았습니다. @@ -612,6 +1031,11 @@ 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 c1250157d31..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 @@ -22,6 +22,16 @@ 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. Magazyn serializacji jest zamknięty. Do zamkniętego magazynu nie można dodawać nowych obiektów. @@ -37,26 +47,6 @@ Ten typ magazynu serializacji nie jest obsługiwany. Użyj magazynu zwracanego przez metodę CreateStore. - - Adding event '{0}' - Dodawanie zdarzenia '{0}' - - - - Unhandled VT: {0}. - Nieobsługiwany VT: {0}. - - - - Double cannot be converted to a date. - Nie można przekonwertować typu double na typ date. - - - - Integer cannot be converted to a float. - Nie można przekonwertować typu integer na typ float. - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + Dodawanie zdarzenia '{0}' + + + + Unhandled VT: {0}. + Nieobsługiwany VT: {0}. + + + + Double cannot be converted to a date. + Nie można przekonwertować typu double na typ date. + + + + Integer cannot be converted to a float. + Nie można przekonwertować typu integer na typ 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,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 Składnik platformy .NET @@ -362,6 +481,16 @@ 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. Dostawca rozszerzony {0} został już dodany jako rozszerzenie. Dodanie kolejnego dostawcy spowodowałoby zduplikowanie właściwości. @@ -372,14 +501,14 @@ Tylko do odczytu - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ Zmienna '{0}' nie została zadeklarowana albo nie została nigdy przypisana. + + &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 powinien być niepustym ciągiem. @@ -522,11 +941,31 @@ Typu danych {0} nie można serializować. Elementy dodane do słownika właściwości muszą mieć możliwość serializowania. + + 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. Typ „{0}” jest niedostępny w strukturze docelowej. + + Error + Error + + Add Component Dodaj składnik @@ -572,26 +1011,6 @@ Usługa {0} jest wymagana, ale nie można jej znaleźć. Jeśli została usunięta, pamiętaj o zapewnieniu jej zamiennika. - - 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 - - - - Error - Error - - Not implemented. Nie zaimplementowano. @@ -612,6 +1031,11 @@ 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 bef419a9c47..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 @@ -22,6 +22,16 @@ 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. O repositório de serialização está fechado. Não é possível adicionar novos objetos a um repositório fechado. @@ -37,26 +47,6 @@ Não há suporte para esse tipo de repositório de serialização. Use um repositório retornado pelo método CreateStore. - - Adding event '{0}' - Adicionando evento '{0}' - - - - Unhandled VT: {0}. - VT não tratado: {0}. - - - - Double cannot be converted to a date. - Não é possível converter duplo em data. - - - - Integer cannot be converted to a float. - Não é possível converter inteiro em flutuante. - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + Adicionando evento '{0}' + + + + Unhandled VT: {0}. + VT não tratado: {0}. + + + + Double cannot be converted to a date. + Não é possível converter duplo em data. + + + + Integer cannot be converted to a float. + Não é possível converter inteiro em flutuante. + + + + 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,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 Componente .NET @@ -362,6 +481,16 @@ 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. O provedor do extensor {0} já foi adicionado como extensor. A inclusão de outro resultará em propriedades duplicadas. @@ -372,14 +501,14 @@ Somente Leitura - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ A variável '{0}' é não declarada ou nunca foi atribuída. + + &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. O argumento deve ser uma cadeia de caracteres não vazia. @@ -522,11 +941,31 @@ O tipo de dados {0} não pode ser serializado. Itens adicionados a um dicionário de propriedades devem ser serializáveis. + + 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. O tipo '{0}' não está disponível na estrutura de destino. + + Error + Error + + Add Component Adicionar Componente @@ -572,26 +1011,6 @@ O serviço {0} é necessário, mas não foi encontrado. Se você tiver removido esse serviço, forneça um substituto. - - 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 - - - - Error - Error - - Not implemented. Não implementado. @@ -612,6 +1031,11 @@ 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 eea5c15c9d7..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 @@ -22,6 +22,16 @@ 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. Хранилище сериализованных объектов закрыто. Добавление новых объектов в закрытое хранилище невозможно. @@ -37,26 +47,6 @@ Этот тип хранилища сериализованных объектов не поддерживается. Используйте хранилище, возвращенное методом CreateStore. - - Adding event '{0}' - Добавляется событие '{0}' - - - - Unhandled VT: {0}. - Необработанный VT: {0}. - - - - Double cannot be converted to a date. - Тип double нельзя привести к типу date. - - - - Integer cannot be converted to a float. - Тип integer нельзя привести к типу float. - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + Добавляется событие '{0}' + + + + Unhandled VT: {0}. + Необработанный VT: {0}. + + + + Double cannot be converted to a date. + Тип double нельзя привести к типу date. + + + + Integer cannot be converted to a float. + Тип integer нельзя привести к типу 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,16 @@ Определяет, будут ли конструкторы использовать линии выравнивания. Если задано значение true, линии выравнивания используются в качестве направляющих. В случае false используются линии сетки. + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component Компонент .NET @@ -362,6 +481,16 @@ Этот объект 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. Поставщик расширений {0} уже добавлен в качестве расширения. Добавление другого поставщика приведет к появлению повторяющихся свойств. @@ -372,14 +501,14 @@ Только для чтения - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ Переменная '{0}' либо не объявлена, либо ей не было присвоено значение. + + &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. Аргумент не может быть пустой строкой. @@ -522,11 +941,31 @@ Тип данных {0} не является упорядочиваемым. В словарь свойств можно добавлять только упорядочиваемые элементы. + + 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. Тип "{0}" недоступен в целевой платформе. + + Error + Error + + Add Component Добавить компонент @@ -572,26 +1011,6 @@ Требуется служба {0}, но она не найдена. Если вы перенесли эту службу, убедитесь в том, что обеспечили замену. - - 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 - - - - Error - Error - - Not implemented. Не реализовано. @@ -612,6 +1031,11 @@ 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 9ed33df6fde..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 @@ -22,6 +22,16 @@ 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. Serileştirme deposu kapalı. Kapalı depoya yeni nesneler eklenemez. @@ -37,26 +47,6 @@ Bu tür serileştirme deposu desteklenmiyor. CreateStore metodu tarafından döndürülen bir depo kullanın. - - Adding event '{0}' - '{0}' olayı ekleniyor - - - - Unhandled VT: {0}. - İşlenmemiş VT: {0}. - - - - Double cannot be converted to a date. - Double tarihe dönüştürülemez. - - - - Integer cannot be converted to a float. - Tam sayı kayan noktalıya dönüştürülemez. - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + '{0}' olayı ekleniyor + + + + Unhandled VT: {0}. + İşlenmemiş VT: {0}. + + + + Double cannot be converted to a date. + Double tarihe dönüştürülemez. + + + + Integer cannot be converted to a float. + Tam sayı kayan noktalıya dönüştürülemez. + + + + 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,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 Bileşeni @@ -362,6 +481,16 @@ 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. {0} uzatma sağlayıcısı zaten bir uzatma olarak eklendi. Bir uzatma daha eklemek yinelenen özelliklere neden olabilir. @@ -372,14 +501,14 @@ Salt Okunur - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ '{0}' değişkeni bildirilmemiş ya da hiç değer atanmamış. + + &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. Bağımsız değişken boş olmayan bir dize olmalıdır. @@ -522,11 +941,31 @@ {0} veri türü seri hale getirilemez. Özellik sözlüğüne eklenen öğelerin seri hale getirilebilir olması gerekir. + + 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. Hedef çerçevede '{0}' türü yok. + + Error + Error + + Add Component Bileşen Ekle @@ -572,26 +1011,6 @@ {0} hizmeti gerekiyordu ancak bulunamadı. Bu hizmeti kaldırdıysanız bunun yerine geçecek bir hizmet sağladığınızdan emin olun. - - 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 - - - - Error - Error - - Not implemented. Uygulanmadı. @@ -612,6 +1031,11 @@ 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 f40558f63b4..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 @@ -22,6 +22,16 @@ 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. 序列化存储区已关闭。新对象无法添加到已关闭的存储区中。 @@ -37,26 +47,6 @@ 不支持这种类型的序列化存储区。请使用 CreateStore 方法返回的存储区。 - - Adding event '{0}' - 添加事件“{0}” - - - - Unhandled VT: {0}. - 未处理的 VT: {0}。 - - - - Double cannot be converted to a date. - 无法将 double 转换为 date。 - - - - Integer cannot be converted to a float. - 无法将 integer 转换为 float。 - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + 添加事件“{0}” + + + + Unhandled VT: {0}. + 未处理的 VT: {0}。 + + + + Double cannot be converted to a date. + 无法将 double 转换为 date。 + + + + Integer cannot be converted to a float. + 无法将 integer 转换为 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,16 @@ 控制设计器是否应使用对齐线。如果设置为 True,则使用对齐线作为参考线。如果设置为 False,则使用网格线。 + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET 组件 @@ -362,6 +481,16 @@ 此 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. 扩展程序提供程序 {0} 已添加为扩展程序。添加其他扩展程序提供程序将导致重复的属性。 @@ -372,14 +501,14 @@ 只读 - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ 变量“{0}”未声明或从未赋值。 + + &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. 参数应为非空的字符串。 @@ -522,11 +941,31 @@ 数据类型 {0} 无法序列化。添加到属性字典中的项必须能序列化。 + + 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. 类型“{0}”在目标框架中不可用。 + + Error + Error + + Add Component 添加组件 @@ -572,26 +1011,6 @@ 未能找到所需服务 {0}。如果您移除了此服务,请确保提供替代服务。 - - 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 - - - - Error - Error - - Not implemented. 未实现。 @@ -612,6 +1031,11 @@ 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 d9fd8325d89..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 @@ -22,6 +22,16 @@ 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. 序列化存放區已關閉。新物件無法加入已關閉的存放區。 @@ -37,26 +47,6 @@ 不支援這種類型的序列化存放區。請使用 CreateStore 方法傳回的存放區。 - - Adding event '{0}' - 正在加入事件 '{0}' - - - - Unhandled VT: {0}. - 未處理的 VT: {0}。 - - - - Double cannot be converted to a date. - Double 無法轉換為 Date。 - - - - Integer cannot be converted to a float. - Integer 無法轉換為 Float。 - - Format {0} components (alignment) Format {0} components (alignment) @@ -78,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} @@ -107,6 +99,121 @@ Unknown spacing command + + Adding event '{0}' + 正在加入事件 '{0}' + + + + Unhandled VT: {0}. + 未處理的 VT: {0}。 + + + + Double cannot be converted to a date. + Double 無法轉換為 Date。 + + + + Integer cannot be converted to a float. + Integer 無法轉換為 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. + + 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} + + Could not convert value '{0}' to the type '{1}'. Could not convert value '{0}' to the type '{1}'. @@ -168,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} @@ -337,6 +446,16 @@ 控制設計工具是否應使用對齊線。如果為 true,就會使用對齊線來對齊。如果為 false,就會使用格線。 + + Dock in Parent Container + Dock in Parent Container + + + + Undock in Parent Container + Undock in Parent Container + + .NET Component .NET 元件 @@ -362,6 +481,16 @@ 此 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. 已經將擴充性提供者 {0} 加入做為擴充項。加入另一個會產生重複的屬性。 @@ -372,14 +501,14 @@ 唯讀 - - Size {0} - Size {0} + + '{1}' is not a valid value for '{0}'. + '{1}' is not a valid value for '{0}'. - - Size {0} components - Size {0} components + + '{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}. @@ -502,6 +631,296 @@ 變數名稱 '{0}' 尚未宣告或沒有指派。 + + &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. 引數必須是非空白字串。 @@ -522,11 +941,31 @@ 資料類型 {0} 不可序列化。要加入至屬性字典的項目必須是可序列化的。 + + 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. 類型 '{0}' 不適用於此目標 Framework。 + + Error + Error + + Add Component 加入元件 @@ -572,26 +1011,6 @@ 找不到必要的服務 {0}。如果您已移除這項服務,請務必提供替代的服務。 - - 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 - - - - Error - Error - - Not implemented. 未實作。 @@ -612,6 +1031,11 @@ 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 67c75436f47..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 @@ -26,6 +26,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 ccbc6717169..bf76439cd04 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/Windows/Forms/Design/DesignerActionHeaderItem.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionHeaderItem.cs similarity index 100% rename from src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionHeaderItem.cs rename to src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionHeaderItem.cs index ed44f3cbedf..860ca085afe 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionHeaderItem.cs +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionHeaderItem.cs @@ -9,9 +9,9 @@ internal 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..6dba5de5dc0 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs @@ -0,0 +1,2986 @@ +// 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 + { + 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; + 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; + } + + 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.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.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) + { + } + + 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); + } + + 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; + 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; + } + } + + 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; + try + { + OnValueChanged(); + } + finally + { + _pushingValue = false; + } + } + } + + 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.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.KeyDown += new KeyEventHandler(OnReadOnlyTextBoxLabelKeyDown); + + _textBox = new TextBox + { + BorderStyle = BorderStyle.None, + TextAlign = System.Windows.Forms.HorizontalAlignment.Left, + Visible = false + }; + _textBox.KeyDown += new KeyEventHandler(OnTextBoxKeyDown); + + 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.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.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(); + 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) + { + Application.DoEvents(); + 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(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) + { + NativeMethods.CommonHandles.GdiHandleCollector.Remove(); + return IntDeleteObject(hObject); + } + + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] + public static extern bool ReleaseCapture(); + + [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + public static extern IntPtr SelectObject(HandleRef hDC, HandleRef hObject); + + [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(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(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) + { + 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(ExternDll.User32, CharSet = CharSet.Auto)] + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + public static extern IntPtr GetWindowLong(HandleRef hWnd, int nIndex); + + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + public static extern IntPtr SetWindowLong(HandleRef hWnd, int nIndex, HandleRef dwNewLong); + + [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(ExternDll.User32, CharSet = CharSet.Auto)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam); + + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] + public static extern IntPtr GetCapture(); + + [DllImport(ExternDll.User32, 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(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) + { + 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) + { + } + + 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/Windows/Forms/Design/DesignerActionTextItem.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionTextItem.cs similarity index 100% rename from src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionTextItem.cs rename to src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionTextItem.cs 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/Windows/Forms/Design/DesignerActionUIStateChangeEventArgs.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventArgs.cs similarity index 100% rename from src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUIStateChangeEventArgs.cs rename to src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventArgs.cs diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUIStateChangeEventHandler.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventHandler.cs similarity index 100% rename from src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUIStateChangeEventHandler.cs rename to src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeEventHandler.cs diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUIStateChangeType.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeType.cs similarity index 100% rename from src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionUIStateChangeType.cs rename to src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionUIStateChangeType.cs 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 a74ba881d70..b69c969dc9b 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/Windows/Forms/Design/AdornmentType.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/AdornmentType.cs index c870a5b170d..bbcf7cebd91 100644 --- 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 @@ -13,12 +13,10 @@ 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. /// 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 01af9af4601..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 @@ -63,15 +63,13 @@ public GlyphCollection Glyphs get => _glyphs; } + /// /// /// /// Forces the BehaviorService to refresh its AdornerWindow. /// public void Invalidate() { - if (_behaviorService != null) - { - _behaviorService.Invalidate(); - } + _behaviorService?.Invalidate(); } /// @@ -79,7 +77,10 @@ public void Invalidate() /// public void Invalidate(Rectangle rectangle) { - _behaviorService?.Invalidate(rectangle); + if (_behaviorService != null) + { + _behaviorService.Invalidate(rectangle); + } } /// 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 87ae06fa40d..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 @@ -40,6 +40,7 @@ public sealed class BehaviorService : IDisposable 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() @@ -53,8 +54,10 @@ public sealed class BehaviorService : IDisposable 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 @@ -105,6 +108,9 @@ internal BehaviorService(IServiceProvider serviceProvider, Control windowFrame) //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.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); } /// /// Read-only property that returns the AdornerCollection that the BehaivorService manages. @@ -122,6 +128,11 @@ internal Control AdornerWindowControl get => _adornerWindow; } + internal int AdornerWindowIndex + { + get => _adornerWindowIndex; + } + internal bool HasCapture { get => _captureBehavior != null; @@ -150,6 +161,8 @@ internal bool UseSnapLines /// public Graphics AdornerWindowGraphics { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] get { Graphics result = _adornerWindow.CreateGraphics(); @@ -173,6 +186,11 @@ public Behavior CurrentBehavior } } + internal bool Dragging + { + get => _dragging; + } + internal bool CancelDrag { get => _cancelDrag; @@ -215,6 +233,96 @@ public void Dispose() } _adornerWindow.Dispose(); + 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); + } } /// @@ -427,6 +535,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. @@ -458,7 +572,7 @@ public void PushCaptureBehavior(Behavior behavior) _captureBehavior = behavior; _adornerWindow.Capture = true; - // 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. + //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) { @@ -534,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; } } @@ -974,7 +1088,7 @@ private class MouseHook private IntPtr _mouseHookHandle = IntPtr.Zero; private bool _processingMessage; - private bool _isHooked = false; + private bool _isHooked = false; //VSWHIDBEY # 474112 private int _lastLButtonDownTimeStamp; public MouseHook() @@ -1010,7 +1124,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); @@ -1109,7 +1223,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) { @@ -1126,8 +1240,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(); @@ -1179,7 +1292,7 @@ private bool PropagateHitTest(Point pt) 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. + // 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 @@ -1284,6 +1397,11 @@ DesignerVerbCollection IMenuCommandService.Verbs } } + internal void StartDragNotification() + { + _adornerWindow.StartDragNotification(); + } + private MenuCommand FindCommand(CommandID commandID, IMenuCommandService menuService) { Behavior behavior = GetAppropriateBehavior(_hitTestedGlyph); @@ -1410,7 +1528,7 @@ private void OnDragEnter(Glyph g, DragEventArgs e) 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. + // 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(); @@ -1541,7 +1659,6 @@ private void OnDragDrop(DragEventArgs e) 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++) @@ -1561,7 +1678,7 @@ private void PropagatePaint(PaintEventArgs pe) [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] private void TestHook_GetRecentSnapLines(ref Message m) { - string snapLineInfo = string.Empty; + string snapLineInfo = ""; if (_testHook_RecentSnapLines != null) { foreach (string line in _testHook_RecentSnapLines) @@ -1594,13 +1711,13 @@ private void TestHook_SetText(ref Message m, string text) if (Marshal.SystemDefaultCharSize == 1) { - bytes = Text.Encoding.Default.GetBytes(text); - nullBytes = Text.Encoding.Default.GetBytes(nullChar); + bytes = System.Text.Encoding.Default.GetBytes(text); + nullBytes = System.Text.Encoding.Default.GetBytes(nullChar); } else { - bytes = Text.Encoding.Unicode.GetBytes(text); - nullBytes = Text.Encoding.Unicode.GetBytes(nullChar); + bytes = System.Text.Encoding.Unicode.GetBytes(text); + nullBytes = System.Text.Encoding.Unicode.GetBytes(nullChar); } Marshal.Copy(bytes, 0, m.LParam, bytes.Length); @@ -1634,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/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..7012cc0b1e0 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/ContainerSelectorGlyph.cs @@ -0,0 +1,74 @@ +// 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/DesignerActionBehavior.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DesignerActionBehavior.cs index 3fcbffcc742..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 @@ -12,9 +12,7 @@ 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. + /// 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 { @@ -36,8 +34,7 @@ internal DesignerActionBehavior(IServiceProvider serviceProvider, IComponent rel } /// - /// Returns the collection of DesignerActionLists this Behavior is managing. - /// These will be dynamically updated (some can be removed, new ones can be added, etc...). + /// 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 { @@ -69,13 +66,11 @@ 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; @@ -86,13 +81,11 @@ internal DesignerActionPanel CreateDesignerActionPanel(IComponent relatedCompone /// 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); } @@ -113,11 +106,10 @@ public override bool OnMouseDoubleClick(Glyph g, MouseButtons button, Point mous } 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. /// 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 0955fae153d..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,51 +3,45 @@ // 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 { /// - /// 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. + /// 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 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 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; + 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. + /// 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, Adorner adorner) : this(behavior, adorner, Rectangle.Empty, null) { } - public DesignerActionGlyph(DesignerActionBehavior behavior, Rectangle alternativeBounds, Control alternativeParent) : - this(behavior, null, alternativeBounds, alternativeParent) + 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) + private DesignerActionGlyph(DesignerActionBehavior behavior, Adorner adorner, Rectangle alternativeBounds, Control alternativeParent) : base(behavior) { - this.adorner = adorner; - this.alternativeBounds = alternativeBounds; - this.alternativeParent = alternativeParent; + _adorner = adorner; + _alternativeBounds = alternativeBounds; + _alternativeParent = alternativeParent; Invalidate(); } @@ -56,33 +50,25 @@ private DesignerActionGlyph(DesignerActionBehavior behavior, Adorner adorner, Re /// public override Rectangle Bounds { - get - { - return bounds; - } + get => _bounds; } + public DockStyle DockEdge { - get - { - return dockStyle; - } + get => _dockStyle; set { - if (dockStyle != value) + if (_dockStyle != value) { - dockStyle = value; + _dockStyle = value; } } } public bool IsInComponentTray { - get - { - return (adorner == null); // adorner and alternative bounds are exclusive - } + get => (_adorner == null); // adorner and alternative bounds are exclusive } /// @@ -90,12 +76,11 @@ public bool IsInComponentTray /// public override Cursor GetHitTest(Point p) { - if (bounds.Contains(p)) + if (_bounds.Contains(p)) { MouseOver = true; return Cursors.Default; } - MouseOver = false; return null; } @@ -107,17 +92,16 @@ private Image GlyphImageClosed { get { - if (glyphImageClosed == null) + if (_glyphImageClosed == null) { - glyphImageClosed = new Bitmap(typeof(DesignerActionGlyph), "Close_left.bmp"); - glyphImageClosed.MakeTransparent(Color.Magenta); + _glyphImageClosed = new Bitmap(typeof(DesignerActionGlyph), "Close_left.bmp"); + _glyphImageClosed.MakeTransparent(Color.Magenta); if (DpiHelper.IsScalingRequired) { - DpiHelper.ScaleBitmapLogicalToDevice(ref glyphImageClosed); + DpiHelper.ScaleBitmapLogicalToDevice(ref _glyphImageClosed); } } - - return glyphImageClosed; + return _glyphImageClosed; } } @@ -125,86 +109,76 @@ private Image GlyphImageOpened { get { - if (glyphImageOpened == null) + if (_glyphImageOpened == null) { - glyphImageOpened = new Bitmap(typeof(DesignerActionGlyph), "Open_left.bmp"); - glyphImageOpened.MakeTransparent(Color.Magenta); + _glyphImageOpened = new Bitmap(typeof(DesignerActionGlyph), "Open_left.bmp"); + _glyphImageOpened.MakeTransparent(Color.Magenta); if (DpiHelper.IsScalingRequired) { - DpiHelper.ScaleBitmapLogicalToDevice(ref glyphImageOpened); + DpiHelper.ScaleBitmapLogicalToDevice(ref _glyphImageOpened); } } - - return glyphImageOpened; + return _glyphImageOpened; } } + internal void InvalidateOwnerLocation() { - if (alternativeParent != null) + if (_alternativeParent != null) { // alternative parent and adoner are exclusive... - alternativeParent.Invalidate(bounds); + _alternativeParent.Invalidate(_bounds); } else { - adorner.Invalidate(bounds); + _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. + /// 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) + if (relatedComponent is Control relatedControl && !(relatedComponent is ToolStripDropDown) && _adorner != null) { - topRight = adorner.BehaviorService.ControlToAdornerWindow(relatedControl); + 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 + // 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) + if (_alternativeParent is ComponentTray compTray) { ComponentTray.TrayControl trayControl = compTray.GetTrayControlFromComponent(relatedComponent); if (trayControl != null) { - alternativeBounds = trayControl.Bounds; + _alternativeBounds = trayControl.Bounds; } } - Rectangle newRect = DesignerUtils.GetBoundsForNoResizeSelectionType(alternativeBounds, SelectionBorderGlyphType.Top); + 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)); - - + _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. + /// 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 - { - return mouseOver; - } + get => _mouseOver; set { - if (mouseOver != value) + if (_mouseOver != value) { - mouseOver = value; + _mouseOver = value; InvalidateOwnerLocation(); } @@ -212,21 +186,19 @@ private bool MouseOver } /// - /// 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'. + /// 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) + if (_insidePaint) { return; } - IComponent panelComponent = ((DesignerActionUI)((DesignerActionBehavior)Behavior).ParentUI).LastPanelComponent; IComponent relatedComponent = ((DesignerActionBehavior)Behavior).RelatedComponent; - if (panelComponent != null && panelComponent == relatedComponent) { image = GlyphImageOpened; @@ -237,16 +209,16 @@ public override void Paint(PaintEventArgs pe) } try { - insidePaint = true; - pe.Graphics.DrawImage(image, bounds.Left, bounds.Top); + _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)); + pe.Graphics.FillRectangle(DesignerUtils.HoverBrush, Rectangle.Inflate(_bounds, -1, -1)); } } finally { - insidePaint = false; + _insidePaint = false; } } } @@ -256,7 +228,7 @@ public override void Paint(PaintEventArgs pe) /// internal void UpdateAlternativeBounds(Rectangle newBounds) { - alternativeBounds = 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 index 7cc34cde7a5..60db5abacd8 100644 --- 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 @@ -12,71 +12,49 @@ 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. + /// 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 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 snaplines on the designer's surface excluding the targetControl. - // All SnapLine coords in these lists have been properly adjusted for the AdornerWindow coords. + // 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 control has. When compiling our global SnapLine lists, if we see a SnapLineType that doesn't exist on our target - we can safely ignore it + // 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 distance overall + // 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; - - // These 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. + //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. + // 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 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 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) { @@ -90,7 +68,8 @@ internal DragAssistanceManager(IServiceProvider serviceProvider, ArrayList dragC } /// - /// Internal constructor that takes the service provider, the list of dragCompoents, and a boolean indicating that we are resizing. + /// 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) { @@ -149,8 +128,7 @@ internal DragAssistanceManager(IServiceProvider serviceProvider, Graphics graphi } /// - /// 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. + /// 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) { @@ -166,19 +144,17 @@ private void AddSnapLines(ControlDesigner controlDesigner, ArrayList horizontalL 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. + // 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 + 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 + //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 + //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; @@ -220,7 +196,7 @@ private void AddSnapLines(ControlDesigner controlDesigner, ArrayList horizontalL } /// - /// 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. + /// 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) { @@ -240,7 +216,7 @@ private int BuildDistanceArray(ArrayList snapLines, ArrayList targetSnapLines, i } } - int smallestDelta = INVALID_VALUE; + int smallestDelta = INVALID_VALUE; //some large # for (int j = 0; j < targetSnapLines.Count; j++) { SnapLine targetSnapLine = (SnapLine)targetSnapLines[j]; @@ -257,12 +233,7 @@ private int BuildDistanceArray(ArrayList snapLines, ArrayList targetSnapLines, i 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. + //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))) { @@ -285,20 +256,18 @@ private Line[] EraseOldSnapLines(Line[] lines, ArrayList tempLines) { for (int i = 0; i < lines.Length; i++) { - Line line = lines[i]; + Rectangle invalidRect = Rectangle.Empty; bool foundMatch = false; - Rectangle invalidRect; + 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. + // 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) { @@ -349,7 +318,6 @@ private Line[] EraseOldSnapLines(Line[] lines, ArrayList tempLines) lines = new Line[0]; } return lines; - } internal void EraseSnapLines() @@ -359,8 +327,7 @@ internal void EraseSnapLines() } /// - /// 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. + /// 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() { @@ -373,8 +340,7 @@ internal Line[] GetRecentLines() private void IdentifyAndStoreValidLines(ArrayList snapLines, int[] distances, Rectangle dragBounds, int smallestDistance) { - int highestPriority = 1; - + int highestPriority = 1; //low //identify top pri for (int i = 0; i < distances.Length; i++) { @@ -382,19 +348,19 @@ private void IdentifyAndStoreValidLines(ArrayList snapLines, int[] distances, Re { int pri = (int)((SnapLine)snapLines[i]).Priority; if ((pri > highestPriority) && (pri != (int)SnapLinePriority.Always)) - {// Always is a special category + { // Always is a special category highestPriority = pri; } } } - // store all snapLines equal to the smallest distance (of the highest priority) + //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. + { //always render SnapLines with Priority.Always which has the same distance. StoreSnapLine((SnapLine)snapLines[i], dragBounds); } } @@ -403,13 +369,13 @@ private void IdentifyAndStoreValidLines(ArrayList snapLines, int[] distances, Re // 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 + 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 + { //control itself has to be visible -- we do mean visible, not ShadowedVisible return false; } @@ -444,26 +410,21 @@ private bool AddControlSnaplinesWhenResizing(ControlDesigner designer, Control c } /// - /// Initializes our class - we cache all snap lines for every control we can find. This is done for perf. reasons. + /// 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) + //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); @@ -480,19 +441,18 @@ private void Initialize(ArrayList dragComponents, IDesignerHost host) if (targetControl != null) { bool disposeDesigner = false; - // get all the target snapline information we need to create one then + //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 + //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) { @@ -500,7 +460,7 @@ private void Initialize(ArrayList dragComponents, IDesignerHost host) } } - // get SnapLines for all our children (nested too) off our root control + //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)) @@ -517,7 +477,6 @@ private void Initialize(ArrayList dragComponents, IDesignerHost host) // 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); @@ -530,7 +489,8 @@ private void Initialize(ArrayList dragComponents, IDesignerHost host) } } } - //Now that we know how many snaplines everyone has, we can create temp arrays now. Intentionally avoiding this on every mousemove. + + // 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]; } @@ -544,7 +504,6 @@ private static bool IsChildOfParent(Control child, Control parent) { return false; } - Control currentParent = child.Parent; while (currentParent != null) { @@ -557,15 +516,12 @@ private static bool IsChildOfParent(Control child, Control 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)); + return snapLine.Filter != null && (snapLine.Filter.StartsWith(SnapLine.Margin) || snapLine.Filter.StartsWith(SnapLine.Padding)); } /// @@ -575,7 +531,6 @@ internal Point OffsetToNearestSnapLocation(Control targetControl, IList targetSn { _targetHorizontalSnapLines.Clear(); _targetVerticalSnapLines.Clear(); - //manually add our snaplines as targets foreach (SnapLine snapline in targetSnaplines) { @@ -589,7 +544,6 @@ internal Point OffsetToNearestSnapLocation(Control targetControl, IList targetSn } } return OffsetToNearestSnapLocation(targetControl, directionOffset); - } /// @@ -599,20 +553,17 @@ internal Point OffsetToNearestSnapLocation(Control targetControl, Point directio { 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 + {//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 + //store off the line structs for actual rendering IdentifyAndStoreValidLines(_verticalSnapLines, _verticalDistances, currentBounds, offset.X); if (directionOffset.X < 0) { @@ -621,18 +572,16 @@ internal Point OffsetToNearestSnapLocation(Control targetControl, Point directio } } if (directionOffset.Y != 0) - { // movement somewhere in the y dir first, build up our distance array + {//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 + //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 + //store off the line structs for actual rendering IdentifyAndStoreValidLines(_horizontalSnapLines, _horizontalDistances, currentBounds, offset.Y); if (directionOffset.Y < 0) { @@ -643,7 +592,7 @@ internal Point OffsetToNearestSnapLocation(Control targetControl, Point directio if (!offset.IsEmpty) { - // setup the cached info for drawing + //setup the cached info for drawing _cachedDragRect = currentBounds; _cachedDragRect.Offset(offset.X, offset.Y); if (offset.X != 0) @@ -657,22 +606,25 @@ internal Point OffsetToNearestSnapLocation(Control targetControl, Point directio _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 - int snapLineIndex = SmallestDistanceIndex(distances, direction, out int distanceValue); + 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 @@ -683,23 +635,19 @@ private static int FindSmallestValidDistance(ArrayList snapLines, int[] distance return 0; } - private static bool IsWithinValidRange(int offset, int min, int max) - { - return offset > min && offset < max; - } + 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 + //check for valid array if (distances.Length == 0) { return smallestIndex; } - // find the next smallest + //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 @@ -719,10 +667,9 @@ private static int SmallestDistanceIndex(int[] distances, int direction, out int if (smallestIndex < distances.Length) { - // return and clear the smallest one we found + //return and clear the smallest one we found distances[smallestIndex] = INVALID_VALUE; } - return smallestIndex; } @@ -731,15 +678,14 @@ private static int SmallestDistanceIndex(int[] distances, int direction, out int /// private void RenderSnapLines(Line[] lines, Rectangle dragRect) { + Pen currentPen; for (int i = 0; i < lines.Length; i++) { - Pen currentPen; if (lines[i].LineType == LineType.Margin || lines[i].LineType == LineType.Padding) { currentPen = _edgePen; - if (lines[i].x1 == lines[i].x2) - { //vertical margin + {//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; @@ -759,10 +705,10 @@ private void RenderSnapLines(Line[] lines, Rectangle dragRect) lines[i].x1 = dragRect.Right; lines[i].x2 = lines[i].OriginalBounds.Right; } - lines[i].x2--; // off by 1 adjust + lines[i].x2--; //off by 1 adjust } else - { // horizontal margin + {//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; @@ -782,25 +728,25 @@ private void RenderSnapLines(Line[] lines, Rectangle dragRect) lines[i].y1 = dragRect.Bottom; lines[i].y2 = lines[i].OriginalBounds.Bottom; } - lines[i].y2--; // off by 1 adjust + lines[i].y2--; //off by 1 adjust } } else if (lines[i].LineType == LineType.Baseline) { currentPen = _baselinePen; - lines[i].x2 -= 1; // off by 1 adjust + lines[i].x2 -= 1; //off by 1 adjust } else { - // default to edgePen + //default to edgePen currentPen = _edgePen; if (lines[i].x1 == lines[i].x2) { - lines[i].y2--; // off by 1 adjustment + lines[i].y2--; //off by 1 adjustment } else { - lines[i].x2--; // off by 1 adjustment + lines[i].x2--; //off by 1 adjustment } } _graphics.DrawLine(currentPen, lines[i].x1, lines[i].y1, lines[i].x2, lines[i].y2); @@ -856,7 +802,6 @@ private void StoreSnapLine(SnapLine snapLine, Rectangle dragBounds) { LineType = type }; - // Performance improvement: Check if the newly added line overlaps existing lines and if so, combine them. CombineSnaplines(line, _tempVertLines); } @@ -867,7 +812,6 @@ private void StoreSnapLine(SnapLine snapLine, Rectangle dragBounds) { LineType = type }; - // Performance improvement: Check if the newly added line overlaps existing lines and if so, combine them. CombineSnaplines(line, _tempHorzLines); } @@ -875,7 +819,7 @@ private void StoreSnapLine(SnapLine snapLine, Rectangle dragBounds) 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 + // 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) @@ -901,8 +845,7 @@ private void StoreSnapLine(SnapLine snapLine, Rectangle dragBounds) } /// - /// 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); + /// 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) @@ -936,7 +879,7 @@ private bool ValidateMarginOrPaddingLine(SnapLine snapLine, Rectangle dragBounds return false; } } - // valid overlapping margin line + //valid overlapping margin line return true; } @@ -955,11 +898,9 @@ internal Point OnMouseMove(Rectangle dragBounds, SnapLine[] snapLines, ref bool { return Point.Empty; } - _targetHorizontalSnapLines.Clear(); _targetVerticalSnapLines.Clear(); - - // manually add our snaplines as targets + //manually add our snaplines as targets foreach (SnapLine snapline in snapLines) { if (snapline.IsHorizontal) @@ -984,8 +925,7 @@ internal Point OnMouseMove(Rectangle dragBounds) } /// - /// Called by the resizebehavior. It needs to know whether we really snapped or not. - /// The snapPoint could be (0,0) even though we snapped. + /// 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) { @@ -1001,12 +941,10 @@ private Point OnMouseMove(Rectangle dragBounds, bool offsetSnapLines, ref bool d { _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 + //offset our targetSnapLines by the amount we have dragged it for (int i = 0; i < _targetHorizontalSnapLines.Count; i++) { ((SnapLine)_targetHorizontalSnapLines[i]).AdjustOffset(_dragOffset.Y); @@ -1017,22 +955,18 @@ private Point OnMouseMove(Rectangle dragBounds, bool offsetSnapLines, ref bool d } } - // First pass - 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 + //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 + //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. + // 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); @@ -1047,23 +981,20 @@ private Point OnMouseMove(Rectangle dragBounds, bool offsetSnapLines, ref bool d 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 - // 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) + //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 + //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) + //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) + //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 /// @@ -1101,9 +1032,7 @@ internal void OnMouseUp() } _behaviorService.RecentSnapLines = lines; } - EraseSnapLines(); - _graphics.Dispose(); if (_disposeEdgePen && _edgePen != null) { @@ -1141,7 +1070,7 @@ public LineType LineType public Rectangle OriginalBounds { get => _originalBounds; - set=> _originalBounds = value; + set => _originalBounds = value; } public PaddingLineType PaddingLineType @@ -1184,7 +1113,6 @@ public static Line[] GetDiffs(Line l1, Line l2) 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; } @@ -1213,7 +1141,6 @@ public static Line Overlap(Line l1, Line l2) { return new Line(Math.Min(l1.x1, l2.x1), l1.y1, Math.Max(l1.x2, l2.x2), l1.y2, l1.LineType); } - return null; } 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 37b3c8aa21c..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 @@ -11,23 +11,18 @@ 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. + /// 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 + 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; @@ -71,6 +66,7 @@ private struct DragComponent private IDesignerHost destHost; private bool currentShowState = true; // Initially the controls are showing + private int primaryComponentIndex = -1; // Index of the primary component (control) in dragComponents /// @@ -78,6 +74,7 @@ private struct DragComponent /// internal DropSourceBehavior(ICollection dragComponents, Control source, Point initialMouseLocation) { + serviceProviderSource = source.Site as IServiceProvider; if (serviceProviderSource == null) { @@ -105,10 +102,10 @@ internal DropSourceBehavior(ICollection dragComponents, Control source, Point in return; } - this.data = new BehaviorDataObject(dragComponents, source, this); - this.allowedEffects = DragDropEffects.Copy | DragDropEffects.None | DragDropEffects.Move; + data = new BehaviorDataObject(dragComponents, source, this); + allowedEffects = DragDropEffects.Copy | DragDropEffects.None | DragDropEffects.Move; this.dragComponents = new DragComponent[dragComponents.Count]; - this.parentGridSize = Size.Empty; + parentGridSize = Size.Empty; lastEffect = DragDropEffects.None; lastFeedbackLocation = new Point(-1, -1); @@ -140,14 +137,12 @@ internal DataObject DataObject private Point AdjustToGrid(Point dragLoc) { //location of the drag with respect to the parent - Point controlLocation = new Point(dragLoc.X - this.parentLocation.X, dragLoc.Y - this.parentLocation.Y); + 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 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; @@ -182,7 +177,6 @@ private Point MapPointFromSourceToTarget(Point pt) } } - private Point MapPointFromTargetToSource(Point pt) { if (srcHost != destHost && destHost != null) @@ -196,10 +190,14 @@ private Point MapPointFromTargetToSource(Point pt) } } + /// + /// This is used to clear the drag images. + /// private void ClearAllDragImages() { if (dragImageRect != Rectangle.Empty) { + Rectangle rect = dragImageRect; rect.Location = MapPointFromSourceToTarget(rect.Location); @@ -220,14 +218,14 @@ private void ClearAllDragImages() } } - // Yeah this is recursive, but we also need to resite all the children of this control, and their children, and their children... + // 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); @@ -245,7 +243,7 @@ private void DropControl(int dragComponentIndex, Control dragTarget, Control dra 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. VSWHIDBEY# 311994 + // 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); } @@ -262,7 +260,7 @@ private void DropControl(int dragComponentIndex, Control dragTarget, Control dra { //between containers dragSource.Controls.Remove(currentControl); - currentControl.Visible = true; + currentControl.Visible = true; dragTarget.Controls.Add(currentControl); } } @@ -272,7 +270,7 @@ private void SetLocationPropertyAndChildIndex(int dragComponentIndex, Control dr 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. + // 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) { @@ -281,7 +279,6 @@ private void SetLocationPropertyAndChildIndex(int dragComponentIndex, Control dr } 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) { @@ -297,7 +294,7 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) { if (!(data.Target is Control dragTarget)) { - return; // can't deal with a non-control drop target yet + 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 @@ -334,15 +331,12 @@ 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); + 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)); @@ -353,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)) { @@ -367,7 +361,6 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) DesignerTransaction transSource = null; DesignerTransaction transTarget = null; string transDesc; - if (dragComponents.Length == 1) { string name = TypeDescriptor.GetComponentName(dragComponents[0].dragComponent); @@ -439,14 +432,14 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) } } - // 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. + // 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 + // 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); @@ -472,8 +465,8 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) } } // everything is fine, carry on... - SetLocationPropertyAndChildIndex(primaryComponentIndex, dragTarget, initialDropPoint, shareParent ? dragComponents[primaryComponentIndex].zorderIndex : 0, allowSetChildIndexOnDrop); - + SetLocationPropertyAndChildIndex(primaryComponentIndex, dragTarget, initialDropPoint, + shareParent ? dragComponents[primaryComponentIndex].zorderIndex : 0, allowSetChildIndexOnDrop); if (selSvc != null) { selSvc.SetSelectedComponents(new object[] { dragComponents[primaryComponentIndex].dragComponent }, SelectionTypes.Primary | SelectionTypes.Replace); @@ -488,8 +481,10 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) } 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); + 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); @@ -513,7 +508,6 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) { dragComponents[i].dragComponent = originalControls[i]; } - originalControls = null; } @@ -537,7 +531,6 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) { listOfTrayControls.Add(tray.Controls[numberOfOriginalTrayControls + i]); } - tray.UpdatePastePositions(listOfTrayControls); } } @@ -549,9 +542,6 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) { transSource.Commit(); transSource = null; -#if PERFORM_AUTO_STUFF - successfulDrag = true; -#endif } if (transTarget != null) { @@ -587,13 +577,14 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) // 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); + statusCommandUITarget.SetStatusInformation(selSvc == null ? dragComponents[primaryComponentIndex].dragComponent as Component : + selSvc.PrimarySelection as Component); } } + // clear the last feedback loc lastFeedbackLocation = new Point(-1, -1); } @@ -603,14 +594,14 @@ private void EndDragDrop(bool allowSetChildIndexOnDrop) /// internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) { - //cache off this last effect so in QueryContinueDrag we can identify (if dropped) a valid drop operation + // 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. + // 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; } @@ -658,6 +649,7 @@ internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) { return; } + targetAllowsSnapLines = true; //check to see if the current designer participate with SnapLines if (newDestHost.GetDesigner(target) is ControlDesigner designer && !designer.ParticipatesWithSnapLines) @@ -671,7 +663,7 @@ internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) { if (destHost != null && destHost != srcHost) { - //re-enable all glyphs in the old host... need to do this before we get the new behaviorservice + // re-enable all glyphs in the old host... need to do this before we get the new behaviorservice behaviorServiceTarget.EnableAllAdorners(true); } @@ -682,7 +674,8 @@ internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) } 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 + + // 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); @@ -697,7 +690,6 @@ internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) 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); @@ -725,11 +717,9 @@ internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) // Always force the dragassistance manager to be created in this case. createNewDragAssistance = true; - destHost = newDestHost; } lastDropTarget = data.Target; - } if (ShowHideDragControls(lastEffect == DragDropEffects.Copy) && !createNewDragAssistance) @@ -751,15 +741,12 @@ internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) //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) { @@ -787,19 +774,14 @@ internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) 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; @@ -808,7 +790,6 @@ internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) newImageRect.Location = MapPointFromSourceToTarget(newImageRect.Location); Rectangle unionRectangle = Rectangle.Union(newImageRect, previousImageRect); - Region invalidRegion = new Region(unionRectangle); invalidRegion.Exclude(newImageRect); @@ -817,17 +798,10 @@ 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(); - if (graphicsTarget != null) { graphicsTarget.SetClip(newImageRect); @@ -835,13 +809,11 @@ internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) graphicsTarget.ResetClip(); } - Control c = dragComponents[primaryComponentIndex].dragComponent as Control; - if (c != null) + 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) { @@ -853,7 +825,7 @@ internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) } } - //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 + // 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(); @@ -865,20 +837,13 @@ internal void GiveFeedback(object sender, GiveFeedbackEventArgs e) 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. + /// 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) + int IComparer.Compare(object x, object y) { DragComponent dc1 = (DragComponent)x; DragComponent dc2 = (DragComponent)y; - if (dc1.zorderIndex > dc2.zorderIndex) { return -1; @@ -906,14 +871,13 @@ private void GetParentSnapInfo(Control parentControl, BehaviorService bhvSvc) if (gridProp != null) { //cache of the gridsize and the location of the parent on the adornerwindow - Control primaryControl = dragComponents[primaryComponentIndex].dragComponent as Control; - if (primaryControl != null) + if (dragComponents[primaryComponentIndex].dragComponent is Control) { - this.parentGridSize = (Size)gridProp.GetValue(parentControl); - this.parentLocation = bhvSvc.MapAdornerWindowPoint(parentControl.Handle, Point.Empty); + parentGridSize = (Size)gridProp.GetValue(parentControl); + parentLocation = bhvSvc.MapAdornerWindowPoint(parentControl.Handle, Point.Empty); if (parentControl.Parent != null && parentControl.Parent.IsMirrored) { - this.parentLocation.Offset(-parentControl.Width, 0); + parentLocation.Offset(-parentControl.Width, 0); } } } @@ -923,7 +887,7 @@ private void GetParentSnapInfo(Control parentControl, BehaviorService bhvSvc) 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 + // 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) @@ -949,26 +913,24 @@ private void DisableAdorners(IServiceProvider serviceProvider, BehaviorService b } /// - /// 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. + /// 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 != null ? primaryControl.Parent : null; + 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); @@ -984,11 +946,9 @@ private void InitiateDrag(Point initialMouseLocation, ICollection dragComps) 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); @@ -1001,9 +961,7 @@ private void InitiateDrag(Point initialMouseLocation, ICollection dragComps) shareParent = false; } } - } - if (shareParent) { Array.Sort(dragComponents, this); @@ -1025,12 +983,11 @@ private void InitiateDrag(Point initialMouseLocation, ICollection dragComps) { 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. + // 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) { @@ -1048,7 +1005,6 @@ private void InitiateDrag(Point initialMouseLocation, ICollection dragComps) { g.Clear(Color.Chartreuse); } - ((Bitmap)dragImage).MakeTransparent(Color.Chartreuse); // Gotta use 2 using's here... Too bad. // Draw each control into the dragimage @@ -1071,21 +1027,18 @@ private void InitiateDrag(Point initialMouseLocation, ICollection dragComps) } } + 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; @@ -1094,7 +1047,6 @@ internal ArrayList GetSortedDragControls(ref int primaryControlIndex) dragControls.Add(dragComponents[i].dragComponent); } } - return dragControls; } @@ -1103,7 +1055,6 @@ internal ArrayList GetSortedDragControls(ref int primaryControlIndex) /// 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) { @@ -1124,7 +1075,6 @@ internal void QueryContinueDrag(object sender, QueryContinueDragEventArgs e) // 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; } - } /// @@ -1132,26 +1082,22 @@ internal void QueryContinueDrag(object sender, QueryContinueDragEventArgs e) /// internal bool ShowHideDragControls(bool show) { - if (currentShowState == show) { return false; } currentShowState = show; - if (dragComponents != null) { for (int i = 0; i < dragComponents.Length; i++) { - Control c = dragComponents[i].dragComponent as Control; - if (c != null) + if (dragComponents[i].dragComponent is Control c) { c.Visible = show; } } } - return true; } @@ -1170,7 +1116,6 @@ internal void CleanupDrag(bool clearImages) } ShowHideDragControls(true); - try { if (suspendedParent != null) @@ -1184,7 +1129,6 @@ internal void CleanupDrag(bool clearImages) suspendedParent = null; //re-enable all glyphs in all adorners behaviorServiceSource.EnableAllAdorners(true); - if (destHost != srcHost && destHost != null) { behaviorServiceTarget.EnableAllAdorners(true); @@ -1220,7 +1164,6 @@ internal void CleanupDrag(bool clearImages) } } } - if (graphicsTarget != null) { graphicsTarget.Dispose(); @@ -1228,61 +1171,48 @@ internal void CleanupDrag(bool clearImages) } cleanedUpDrag = true; } - } } /// - /// This class extends from DataObject and carries additional - /// information such as: the list of Controls currently being - /// dragged and the drag 'Source'. + /// 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 ICollection dragComponents; - private Control source; - private IComponent target; - private DropSourceBehavior sourceBehavior; + private readonly ICollection _dragComponents; + private readonly Control _source; + private IComponent _target; + private readonly DropSourceBehavior _sourceBehavior; public BehaviorDataObject(ICollection dragComponents, Control source, DropSourceBehavior sourceBehavior) : base() { - this.dragComponents = dragComponents; - this.source = source; - this.sourceBehavior = sourceBehavior; - this.target = null; + _dragComponents = dragComponents; + _source = source; + _sourceBehavior = sourceBehavior; + _target = null; } public Control Source { - get => source; + get => _source; } public ICollection DragComponents { - get => dragComponents; + get => _dragComponents; } public IComponent Target { - get => target; - set => target = value; - } - - internal void EndDragDrop(bool allowSetChildIndexOnDrop) - { - sourceBehavior.EndDragDrop(allowSetChildIndexOnDrop); + get => _target; + set => _target = value; } - internal void CleanupDrag() - { - sourceBehavior.CleanupDrag(); - } + internal void EndDragDrop(bool allowSetChildIndexOnDrop) => _sourceBehavior.EndDragDrop(allowSetChildIndexOnDrop); - internal ArrayList GetSortedDragControls(ref int primaryControlIndex) - { - return sourceBehavior.GetSortedDragControls(ref primaryControlIndex); - } + 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..03cd2fe226e --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/NoResizeSelectionBorderGlyph.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; + +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/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/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/SelectionGlyphBase.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/SelectionGlyphBase.cs index fdf80bb9e46..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 @@ -7,7 +7,7 @@ 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. + /// 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 { @@ -30,7 +30,7 @@ public SelectionRules SelectionRules { get => rules; } - + /// /// Simple hit test rule: if the point is contained within the bounds - then it is a positive hit test. /// @@ -42,7 +42,7 @@ public override Cursor GetHitTest(Point p) } return null; } - + /// /// Returns the HitTestCursor for this glyph. /// @@ -50,7 +50,7 @@ public Cursor HitTestCursor { get => hitTestCursor; } - + /// /// The Bounds of this glyph. /// @@ -58,7 +58,7 @@ public override Rectangle Bounds { get => bounds; } - + /// /// There's no paint logic on this base class. /// 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 e40fab32065..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,8 +18,8 @@ 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 + // 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"; 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 index 4e1c9c70827..ba2ee6250ac 100644 --- 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 @@ -17,7 +17,7 @@ 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. + /// 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 { @@ -25,7 +25,6 @@ internal class CommandSet : IDisposable 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 @@ -33,18 +32,15 @@ internal class CommandSet : IDisposable 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 + 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. /// @@ -53,52 +49,47 @@ internal class CommandSet : IDisposable 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(this.OnEventHandlerChanged); + _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(this.UpdateClipboardItems); + 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[] { - + _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), - + 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), - + 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(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), + 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(this.OnSelectionChanged); + _selectionService.SelectionChanged += new EventHandler(OnSelectionChanged); } _menuService = (IMenuCommandService)site.GetService(typeof(IMenuCommandService)); @@ -110,8 +101,7 @@ public CommandSet(ISite site) } } - // 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. + // 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) @@ -146,7 +136,6 @@ protected IMenuCommandService MenuService { _menuService = (IMenuCommandService)GetService(typeof(IMenuCommandService)); } - return _menuService; } } @@ -178,7 +167,7 @@ protected Timer SnapLineTimer { Interval = DesignerUtils.SNAPELINEDELAY }; - _snapLineTimer.Tick += new EventHandler(this.OnSnapLineTimerExpire); + _snapLineTimer.Tick += new EventHandler(OnSnapLineTimerExpire); } return _snapLineTimer; } @@ -197,7 +186,6 @@ private bool CheckComponentEditor(object obj, bool launchEditor) { return true; } - ComponentEditor editor = (ComponentEditor)TypeDescriptor.GetEditor(obj, typeof(ComponentEditor)); if (editor == null) { @@ -206,7 +194,6 @@ private bool CheckComponentEditor(object obj, bool launchEditor) bool success = false; IComponentChangeService changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService)); - if (changeService != null) { try @@ -228,18 +215,15 @@ private bool CheckComponentEditor(object obj, bool launchEditor) } } - WindowsFormsComponentEditor winEditor = editor as WindowsFormsComponentEditor; - if (winEditor != null) + if (editor is WindowsFormsComponentEditor winEditor) { IWin32Window parent = null; - //REVIEW: This smells wrong if (obj is IWin32Window) { #pragma warning disable 1717 // assignment to self parent = (IWin32Window)parent; #pragma warning restore 1717 } - success = winEditor.EditComponent(obj, parent); } else @@ -269,7 +253,7 @@ private bool CheckComponentEditor(object obj, bool launchEditor) /// Disposes of this object, removing all commands from the menu service. /// // We don't need to Dispose snapLineTimer - [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] public virtual void Dispose() { if (_menuService != null) @@ -284,13 +268,13 @@ public virtual void Dispose() if (_selectionService != null) { - _selectionService.SelectionChanged -= new EventHandler(this.OnSelectionChanged); + _selectionService.SelectionChanged -= new EventHandler(OnSelectionChanged); _selectionService = null; } if (_eventService != null) { - _eventService.EventHandlerChanged -= new EventHandler(this.OnEventHandlerChanged); + _eventService.EventHandlerChanged -= new EventHandler(OnEventHandlerChanged); _eventService = null; } @@ -298,13 +282,13 @@ public virtual void Dispose() Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); if (host != null) { - host.Activated -= new EventHandler(this.UpdateClipboardItems); + host.Activated -= new EventHandler(UpdateClipboardItems); } if (_snapLineTimer != null) { _snapLineTimer.Stop(); - _snapLineTimer.Tick -= new EventHandler(this.OnSnapLineTimerExpire); + _snapLineTimer.Tick -= new EventHandler(OnSnapLineTimerExpire); _snapLineTimer = null; } @@ -324,7 +308,6 @@ protected void EndDragManager() { _snapLineTimer.Stop(); } - dragManager.EraseSnapLines(); dragManager.OnMouseUp(); dragManager = null; @@ -332,14 +315,13 @@ protected void EndDragManager() } /// - /// 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. + /// 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) { @@ -361,7 +343,7 @@ private object[] FilterSelection(object[] components, SelectionRules selectionRu } /// - /// Used to retrieve the selection for a copy. The default implementation retrieves the current selection. + /// Used to retrieve the selection for a copy. The default implementation retrieves the current selection. /// protected virtual ICollection GetCopySelection() { @@ -376,7 +358,6 @@ protected virtual ICollection GetCopySelection() sort = true; break; } - } if (sort) { @@ -403,7 +384,6 @@ private void GetAssociatedComponents(IComponent component, IDesignerHost host, A { return; } - foreach (IComponent childComp in designer.AssociatedComponents) { if (childComp.Site != null) @@ -477,9 +457,9 @@ private Size GetSize(IComponent comp) 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 = null; - PropertyDescriptor gridSizeProp = null; - PropertyDescriptor currentSnapProp = null; + IComponent currentSnapComponent; + PropertyDescriptor gridSizeProp; + PropertyDescriptor currentSnapProp; PropertyDescriptorCollection props; currentSnapComponent = host.RootComponent; @@ -489,13 +469,11 @@ protected virtual void GetSnapInformation(IDesignerHost host, IComponent compone { 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; @@ -514,7 +492,9 @@ protected virtual void GetSnapInformation(IDesignerHost host, IComponent compone /// 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 @@ -532,7 +512,7 @@ protected bool CanCheckout(IComponent comp) } /// - /// 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. + /// 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) { @@ -679,7 +659,6 @@ protected virtual void OnKeyMove(object sender, EventArgs e) { 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... @@ -693,8 +672,7 @@ protected virtual void OnKeyMove(object sender, EventArgs e) 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 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()); @@ -702,15 +680,11 @@ protected virtual void OnKeyMove(object sender, EventArgs e) 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 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; @@ -721,13 +695,11 @@ protected virtual void OnKeyMove(object sender, EventArgs e) { 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; @@ -738,17 +710,13 @@ protected virtual void OnKeyMove(object sender, EventArgs e) 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 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) { @@ -759,7 +727,6 @@ protected virtual void OnKeyMove(object sender, EventArgs e) moveOffsetY = loc.Y - primaryControl.Location.Y; } } - } } else @@ -787,7 +754,6 @@ protected virtual void OnKeyMove(object sender, EventArgs e) //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) @@ -796,7 +762,6 @@ protected virtual void OnKeyMove(object sender, EventArgs e) loc.Offset(moveOffsetX, moveOffsetY); propLoc.SetValue(component, loc); } - //change the Status information .... if (component == selSvc.PrimarySelection && _statusCommandUI != null) { @@ -868,7 +833,6 @@ protected void OnMenuAlignByPrimary(object sender, EventArgs e) { continue; } - IComponent comp = obj as IComponent; if (comp != null && host != null) { @@ -877,12 +841,12 @@ protected void OnMenuAlignByPrimary(object sender, EventArgs e) continue; } } - PropertyDescriptorCollection props = TypeDescriptor.GetProperties(comp); -PropertyDescriptor locProp = props["Location"]; + 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)) @@ -890,6 +854,7 @@ protected void OnMenuAlignByPrimary(object sender, EventArgs e) } // Skip all components that don't have a location property + // if (locProp == null || locProp.IsReadOnly) { continue; @@ -957,8 +922,6 @@ protected void OnMenuAlignByPrimary(object sender, EventArgs e) return; } firstTry = false; - - locProp.SetValue(comp, loc); } } @@ -982,11 +945,6 @@ protected void OnMenuAlignByPrimary(object sender, EventArgs e) protected void OnMenuAlignToGrid(object sender, EventArgs e) { Size gridSize = Size.Empty; - PropertyDescriptor locProp = null; - PropertyDescriptor lockedProp = null; - Point loc = Point.Empty; - int delta; - if (SelectionService == null) { return; @@ -1000,7 +958,6 @@ protected void OnMenuAlignToGrid(object sender, EventArgs e) IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); DesignerTransaction trans = null; - try { if (host != null) @@ -1026,12 +983,11 @@ protected void OnMenuAlignToGrid(object sender, EventArgs e) foreach (object comp in selectedComponents) { // first check to see if the component is locked, if so - don't move it... - lockedProp = GetProperty(comp, "Locked"); + 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) @@ -1043,16 +999,17 @@ protected void OnMenuAlignToGrid(object sender, EventArgs e) } // get the location property - locProp = GetProperty(comp, "Location"); + PropertyDescriptor locProp = GetProperty(comp, "Location"); + // get the current value if (locProp == null || locProp.IsReadOnly) { continue; } - loc = (Point)locProp.GetValue(comp); + Point loc = (Point)locProp.GetValue(comp); // round the x to the snap size - delta = loc.X % gridSize.Width; + int delta = loc.X % gridSize.Width; if (delta < (gridSize.Width / 2)) { loc.X -= delta; @@ -1105,7 +1062,6 @@ protected void OnMenuCenterSelection(object sender, EventArgs e) { MenuCommand cmd = (MenuCommand)sender; CommandID cmdID = cmd.CommandID; - if (SelectionService == null) { return; @@ -1128,6 +1084,7 @@ protected void OnMenuCenterSelection(object sender, EventArgs e) if (host != null) { string batchString; + if (cmdID == MenuCommands.CenterHorizontally) { batchString = string.Format(SR.WindowsFormsCommandCenterX, selectedComponents.Count); @@ -1138,12 +1095,11 @@ protected void OnMenuCenterSelection(object sender, EventArgs e) } trans = host.CreateTransaction(batchString); } - - //subhag calculate the union REctangle : ASURT 67753 - int top = Int32.MaxValue; - int left = Int32.MaxValue; - int right = Int32.MinValue; - int bottom = Int32.MinValue; + //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) { @@ -1168,6 +1124,7 @@ protected void OnMenuCenterSelection(object sender, EventArgs e) 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) { @@ -1193,16 +1150,12 @@ protected void OnMenuCenterSelection(object sender, EventArgs e) 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; @@ -1289,7 +1242,6 @@ protected void OnMenuCopy(object sender, EventArgs e) 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) @@ -1320,7 +1272,6 @@ protected void OnMenuCut(object sender, EventArgs e) { return; } - Cursor oldCursor = Cursor.Current; try { @@ -1340,10 +1291,8 @@ protected void OnMenuCut(object sender, EventArgs e) 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)); @@ -1357,16 +1306,16 @@ protected void OnMenuCut(object sender, EventArgs e) 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 see bug 488115 + //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; @@ -1402,6 +1351,7 @@ protected void OnMenuCut(object sender, EventArgs e) else if (commonParent != null && c != null) { Control selectedControl = c; + if (selectedControl.Parent != commonParent && !commonParent.Contains(selectedControl)) { // look for internal parenting @@ -1415,8 +1365,6 @@ protected void OnMenuCut(object sender, EventArgs e) } } } - - if (component != null) { ArrayList al = new ArrayList(); @@ -1425,6 +1373,7 @@ protected void OnMenuCut(object sender, EventArgs e) { changeService.OnComponentChanging(comp, null); } + host.DestroyComponent(component); } } @@ -1440,9 +1389,7 @@ protected void OnMenuCut(object sender, EventArgs e) des.ResumeChangingEvents(); } } - } - if (commonParent != null) { SelectionService.SetSelectedComponents(new object[] { commonParent }, SelectionTypes.Replace); @@ -1473,7 +1420,6 @@ protected void OnMenuDelete(object sender, EventArgs e) { IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || host != null, "IDesignerHost not found"); - if (SelectionService == null) { return; @@ -1520,13 +1466,11 @@ protected void OnMenuDelete(object sender, EventArgs e) { continue; } - // We should never delete the base component. if (obj == host.RootComponent) { continue; } - Control control = obj as Control; if (!commonParentSet) { @@ -1587,9 +1531,7 @@ protected void OnMenuDelete(object sender, EventArgs e) designerChain.Add(designer); } - for (commonParentDesigner = commonParentDesigner.Parent as ITreeDesigner; - commonParentDesigner != null; - commonParentDesigner = commonParentDesigner.Parent as ITreeDesigner) + for (commonParentDesigner = commonParentDesigner.Parent as ITreeDesigner; commonParentDesigner != null; commonParentDesigner = commonParentDesigner.Parent as ITreeDesigner) { parentDesignerChain.Add(commonParentDesigner); } @@ -1613,6 +1555,7 @@ protected void OnMenuDelete(object sender, EventArgs e) longIndex--; } } + // alright, what have we got? if (commonParentDesigner != null) { commonParent = commonParentDesigner.Component; @@ -1623,9 +1566,7 @@ protected void OnMenuDelete(object sender, EventArgs e) } } } - } - ArrayList al = new ArrayList(); GetAssociatedComponents((IComponent)obj, host, al); foreach (IComponent comp in al) @@ -1651,7 +1592,6 @@ protected void OnMenuDelete(object sender, EventArgs e) } } - if (commonParent != null && SelectionService.PrimarySelection == null) { if (host.GetDesigner(commonParent) is ITreeDesigner commonParentDesigner && commonParentDesigner.Children != null) @@ -1709,7 +1649,7 @@ protected void OnMenuDelete(object sender, EventArgs e) /// Called when the paste menu item is selected. /// - [SuppressMessage("Microsoft.Performance", "CA1809:AvoidExcessiveLocals")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1809:AvoidExcessiveLocals")] protected void OnMenuPaste(object sender, EventArgs e) { Cursor oldCursor = Cursor.Current; @@ -1717,13 +1657,12 @@ protected void OnMenuPaste(object sender, EventArgs e) try { Cursor.Current = Cursors.WaitCursor; - // If a control fails to get pasted; then we should remember its associatedComponents so that they are not pasted. + // 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; - + return; // nothing we can do here! IDataObject dataObj = Clipboard.GetDataObject(); ICollection components = null; bool createdItems = false; @@ -1732,7 +1671,6 @@ protected void OnMenuPaste(object sender, EventArgs e) // 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)) @@ -1773,16 +1711,13 @@ protected void OnMenuPaste(object sender, EventArgs e) { 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; @@ -1790,7 +1725,6 @@ protected void OnMenuPaste(object sender, EventArgs e) IComponent baseComponent = host.RootComponent; selectedComponent = (IComponent)SelectionService.PrimarySelection; - if (selectedComponent == null) { selectedComponent = baseComponent; @@ -1798,7 +1732,6 @@ protected void OnMenuPaste(object sender, EventArgs e) dragClient = false; ITreeDesigner tree = host.GetDesigner(selectedComponent) as ITreeDesigner; - while (!dragClient && tree != null) { if (tree is IOleDragClient) @@ -1816,10 +1749,10 @@ protected void OnMenuPaste(object sender, EventArgs e) 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. + // 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) @@ -1847,6 +1780,7 @@ protected void OnMenuPaste(object sender, EventArgs e) { continue; } + if (pd.GetValue(curComp) is string handler) { pd.SetValue(curComp, null); @@ -1877,6 +1811,7 @@ protected void OnMenuPaste(object sender, EventArgs e) } 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; @@ -1932,16 +1867,13 @@ protected void OnMenuPaste(object sender, EventArgs e) { 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. @@ -1950,7 +1882,6 @@ protected void OnMenuPaste(object sender, EventArgs e) changeName = true; } } - if (changeName) { PropertyDescriptorCollection props = TypeDescriptor.GetProperties(curComp); @@ -1970,8 +1901,6 @@ protected void OnMenuPaste(object sender, EventArgs e) } } } - - // Find those controls that have ControlDesigners and center them on the designer surface ArrayList compsWithControlDesigners = new ArrayList(); foreach (Control c in controls) @@ -1985,7 +1914,7 @@ protected void OnMenuPaste(object sender, EventArgs e) 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. + // 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); } @@ -1995,7 +1924,6 @@ protected void OnMenuPaste(object sender, EventArgs e) // 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; @@ -2010,7 +1938,7 @@ protected void OnMenuPaste(object sender, EventArgs e) } } - // 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. + // 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) { @@ -2019,7 +1947,6 @@ protected void OnMenuPaste(object sender, EventArgs e) // 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) { @@ -2108,6 +2035,7 @@ protected void OnMenuShowGrid(object sender, EventArgs e) if (host != null) { DesignerTransaction trans = null; + try { trans = host.CreateTransaction(); @@ -2139,7 +2067,6 @@ protected void OnMenuSizingCommand(object sender, EventArgs e) { MenuCommand cmd = (MenuCommand)sender; CommandID cmdID = cmd.CommandID; - if (SelectionService == null) { return; @@ -2154,7 +2081,6 @@ protected void OnMenuSizingCommand(object sender, EventArgs e) sel.CopyTo(selectedObjects, 0); selectedObjects = FilterSelection(selectedObjects, SelectionRules.Visible); object selPrimary = SelectionService.PrimarySelection; - Size primarySize = Size.Empty; Size itemSize = Size.Empty; PropertyDescriptor sizeProp; @@ -2167,7 +2093,6 @@ protected void OnMenuSizingCommand(object sender, EventArgs e) return; } primarySize = (Size)sizeProp.GetValue(component); - } if (selPrimary == null) { @@ -2190,22 +2115,21 @@ protected void OnMenuSizingCommand(object sender, EventArgs e) 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) { @@ -2256,7 +2180,6 @@ protected void OnMenuSizeToGrid(object sender, EventArgs e) 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; @@ -2289,7 +2212,6 @@ protected void OnMenuSizeToGrid(object sender, EventArgs e) 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; @@ -2297,12 +2219,10 @@ protected void OnMenuSizeToGrid(object sender, EventArgs e) 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); } @@ -2325,7 +2245,6 @@ 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; @@ -2353,7 +2272,6 @@ protected void OnMenuSnapToGrid(object sender, EventArgs e) if (host != null) { DesignerTransaction trans = null; - try { trans = host.CreateTransaction(string.Format(SR.CommandSetPaste, 0)); @@ -2376,6 +2294,7 @@ protected void OnMenuSnapToGrid(object sender, EventArgs e) } } } + } /// @@ -2386,7 +2305,6 @@ protected void OnMenuSpacingCommand(object sender, EventArgs e) MenuCommand cmd = (MenuCommand)sender; CommandID cmdID = cmd.CommandID; DesignerTransaction trans = null; - if (SelectionService == null) { return; @@ -2427,19 +2345,12 @@ protected void OnMenuSpacingCommand(object sender, EventArgs e) 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) + 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) + else if (cmdID == MenuCommands.VertSpaceConcatenate || cmdID == MenuCommands.VertSpaceDecrease || cmdID == MenuCommands.VertSpaceIncrease || cmdID == MenuCommands.VertSpaceMakeEqual) { sort = SORT_VERTICAL; } @@ -2449,7 +2360,6 @@ protected void OnMenuSpacingCommand(object sender, EventArgs e) } SortSelection(selectedObjects, sort); - //now that we're sorted, lets get our primary selection and it's index object primary = SelectionService.PrimarySelection; int primaryIndex = 0; @@ -2457,10 +2367,10 @@ protected void OnMenuSpacingCommand(object sender, EventArgs e) primaryIndex = Array.IndexOf(selectedObjects, primary); // And compute delta values for Make Equal - if (cmdID == MenuCommands.HorizSpaceMakeEqual || cmdID == MenuCommands.VertSpaceMakeEqual) + if (cmdID == MenuCommands.HorizSpaceMakeEqual || + cmdID == MenuCommands.VertSpaceMakeEqual) { int total, n; - total = 0; for (n = 0; n < selectedObjects.Length; n++) { @@ -2579,7 +2489,6 @@ protected void OnMenuSpacingCommand(object sender, EventArgs e) } } curComp = lastComp = null; - if (primary != null) { PropertyDescriptor primaryLocDesc = GetProperty(primary, "Location"); @@ -2753,11 +2662,10 @@ protected void OnMenuSpacingCommand(object sender, EventArgs e) /// protected void OnSelectionChanged(object sender, EventArgs e) { - if (SelectionService == null/*: UNDONE: BehaviorWork || SelectionUIService == null*/) + if (SelectionService == null) { return; } - _selectionVersion++; // Update our cached selection counts. selCount = SelectionService.SelectionCount; @@ -2797,7 +2705,7 @@ protected void OnSelectionChanged(object sender, EventArgs e) } /// - /// 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. + /// 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) { @@ -2809,8 +2717,7 @@ private void OnSnapLineTimerExpire(object sender, EventArgs e) } /// - /// Called when our snapline timer expires - this method has been call - /// has been properly marshalled back to the correct thread. + /// 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) { @@ -2871,7 +2778,7 @@ protected void OnStatusCopy(object sender, EventArgs e) } /// - /// Status for the cut command. This is enabled when there is something juicy selected and that something does not contain any inherited components. + /// 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) { @@ -2903,7 +2810,7 @@ protected void OnStatusDelete(object sender, EventArgs e) 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. + // 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; @@ -2923,7 +2830,7 @@ 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. + // 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"); @@ -2940,7 +2847,7 @@ protected void OnStatusPaste(object sender, EventArgs e) } } - // Not being inherited. Now look at the contents of the data + // Not being inherited. Now look at the contents of the data IDataObject dataObj = Clipboard.GetDataObject(); bool enable = false; if (dataObj != null) @@ -2976,7 +2883,7 @@ protected virtual void OnStatusSelectAll(object sender, EventArgs e) } /// - /// 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. + /// 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() { @@ -3008,7 +2915,6 @@ private ICollection PrependComponentNames(ICollection objects) } newObjects[idx++] = o; } - string[] nameArray = new string[names.Count]; names.CopyTo(nameArray, 0); newObjects[0] = nameArray; @@ -3020,7 +2926,7 @@ private ICollection PrependComponentNames(ICollection objects) /// private void SortSelection(object[] selectedObjects, int nSortBy) { - IComparer comp = null; + IComparer comp; switch (nSortBy) { case SORT_HORIZONTAL: @@ -3090,10 +2996,9 @@ private void UpdatePastePositions(ArrayList controls) 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. + // 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; @@ -3103,11 +3008,10 @@ private void UpdatePastePositions(ArrayList controls) 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. + // 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; @@ -3118,6 +3022,7 @@ private void UpdatePastePositions(ArrayList controls) { 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); } @@ -3168,7 +3073,6 @@ private void UpdatePastePositions(ArrayList controls) { parentOffset.X = 0; parentOffset.Y = 0; - if (wrapped) { bumpIt = false; @@ -3206,7 +3110,6 @@ private void UpdatePastePositions(ArrayList controls) parentControl.ResumeLayout(); } } - } private void UpdatePasteTabIndex(Control componentControl, object parentComponent) @@ -3215,7 +3118,6 @@ private void UpdatePasteTabIndex(Control componentControl, object parentComponen { return; } - bool tabIndexCollision = false; int tabIndexOriginal = componentControl.TabIndex; // Find the next highest tab index @@ -3241,13 +3143,13 @@ private void UpdatePasteTabIndex(Control componentControl, object parentComponen } /// - /// 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. + /// 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 EventHandler _statusHandler; + private readonly EventHandler _statusHandler; private readonly IEventHandlerService _eventService; - private IUIService _uiService; + 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. @@ -3274,8 +3176,7 @@ public CommandSetItem(CommandSet commandSet, EventHandler statusHandler, EventHa _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. + // 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. @@ -3289,14 +3190,14 @@ public CommandSetItem(CommandSet commandSet, EventHandler statusHandler, EventHa } } - // 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. + // 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++; + state.refCount++; } } @@ -3330,7 +3231,6 @@ private void ApplyCachedStatus() { // 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); @@ -3358,7 +3258,6 @@ public override void Invoke() return; } } - base.Invoke(); } catch (Exception e) @@ -3372,7 +3271,6 @@ public override void Invoke() throw; } } - } /// @@ -3403,7 +3301,6 @@ private void SaveCommandStatus() { state = new StatusState(); } - // and save the enabled, visible, checked, and supported state. state.SaveState(this, _commandSet.SelectionVersion); } @@ -3423,7 +3320,6 @@ public void UpdateStatus() 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. @@ -3452,8 +3348,8 @@ public virtual void Dispose() { if (s_commandStatusHash[_statusHandler] is StatusState state) { - state._refCount--; - if (state._refCount == 0) + state.refCount--; + if (state.refCount == 0) { s_commandStatusHash.Remove(_statusHandler); } @@ -3465,6 +3361,7 @@ public virtual void Dispose() /// 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; @@ -3472,11 +3369,12 @@ private class StatusState 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; - // 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; @@ -3522,16 +3420,14 @@ internal void SaveState(CommandSetItem item, int version) } /// - /// 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. + /// 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) + public ImmediateCommandSetItem(CommandSet commandSet, EventHandler statusHandler, EventHandler invokeHandler, CommandID id, IUIService uiService) : base(commandSet, statusHandler, invokeHandler, id, uiService) { } @@ -3559,13 +3455,11 @@ public int Compare(object p, object q) 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; } } @@ -3581,20 +3475,17 @@ public int Compare(object p, object q) 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) @@ -3609,7 +3500,6 @@ public int Compare(object p, object q) { return 0; } - if (!(p is Control c1) || !(q is Control c2)) { return 1; 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 ce1f7336860..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 @@ -27,19 +27,18 @@ 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. + /// Provides drag and drop functionality through OLE. /// internal OleDragDropHandler oleDragDropHandler; // handler class for ole drag drop operations. @@ -53,25 +52,30 @@ public class ComponentTray : ScrollableControl, IExtenderProvider, ISelectionUIH 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 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 + 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 + 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) @@ -82,9 +86,7 @@ public ComponentTray(IDesigner mainDesigner, IServiceProvider 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."); @@ -105,33 +107,28 @@ public ComponentTray(IDesigner mainDesigner, IServiceProvider serviceProvider) 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); + 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); } - IUIService uiService = GetService(typeof(IUIService)) as IUIService; - if (uiService != null) + if (GetService(typeof(IUIService)) is IUIService uiService) { Color styleColor; - if (uiService.Styles["ArtboardBackground"] is Color) { styleColor = (Color)uiService.Styles["ArtboardBackground"]; @@ -176,179 +173,134 @@ 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); - - BehaviorService behSvc = GetService(typeof(BehaviorService)) as BehaviorService; - if (behSvc != null) + if (GetService(typeof(BehaviorService)) is BehaviorService behSvc) { //this object will manage any glyphs that get added to our tray glyphManager = new ComponentTrayGlyphManager(selSvc, behSvc); } } - private InheritanceUI InheritanceUI + + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { - get + if (IsHandleCreated) { - if (inheritanceUI == null) - { - inheritanceUI = new InheritanceUI(); - } - return inheritanceUI; + fResetAmbient = true; + ResetTrayControls(); + BeginInvoke(new AsyncInvokeHandler(Invalidate), new object[] { true }); } } - /// - /// Retrieves the menu editor service, which we cache for speed. - /// - private IMenuCommandService MenuService + private void OnComponentRefresh(RefreshEventArgs e) { - get + if (e.ComponentChanged is IComponent component) { - if (menuCommandService == null) + TrayControl control = TrayControl.FromComponent(component); + if (control != null) { - menuCommandService = (IMenuCommandService)GetService(typeof(IMenuCommandService)); + 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(); + } } - return menuCommandService; } } - private class ComponentTrayGlyphManager + private void OnSystemSettingChanged(object sender, EventArgs e) { - 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; - - public ComponentTrayGlyphManager(ISelectionService selSvc, BehaviorService behaviorSvc) + if (IsHandleCreated) { - _selSvc = selSvc; - _behaviorSvc = behaviorSvc; - _traySelectionAdorner = new Adorner(); + fResetAmbient = true; + ResetTrayControls(); + BeginInvoke(new AsyncInvokeHandler(Invalidate), new object[] { true }); } + } - public GlyphCollection SelectionGlyphs - { - get => _traySelectionAdorner.Glyphs; - } + private void ResetTrayControls() + { + ControlCollection children = (ControlCollection)Controls; + if (children == null) + return; - public void Dispose() + for (int i = 0; i < children.Count; ++i) { - if (_traySelectionAdorner != null) + if (children[i] is TrayControl tc) { - _traySelectionAdorner.Glyphs.Clear(); - _traySelectionAdorner = null; + tc._fRecompute = true; } } + } - 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; - } + private delegate void AsyncInvokeHandler(bool children); - public Cursor GetHitTest(Point p) + private void OnSelectionChanged(object sender, EventArgs e) + { + selectedObjects = ((ISelectionService)sender).GetSelectedComponents(); + object primary = ((ISelectionService)sender).PrimarySelection; + Invalidate(); + fSelectionChanged = true; + // Accessibility information + foreach (object selObj in selectedObjects) { - for (int i = 0; i < _traySelectionAdorner.Glyphs.Count; i++) + if (selObj is IComponent component) { - Cursor hitTestCursor = _traySelectionAdorner.Glyphs[i].GetHitTest(p); - if (hitTestCursor != null) + Control c = TrayControl.FromComponent(component); + if (c != null) { - _hitTestedGlyph = _traySelectionAdorner.Glyphs[i]; - return hitTestCursor; + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "MSAA: SelectionAdd, traycontrol = " + c.ToString()); + UnsafeNativeMethods.NotifyWinEvent((int)AccessibleEvents.SelectionAdd, new HandleRef(c, c.Handle), NativeMethods.OBJID_CLIENT, 0); } } - - _hitTestedGlyph = null; - return null; - } - - 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; - } - - 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; - } - - 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; - } - - public bool OnMouseUp(MouseEventArgs e) - { - if (_hitTestedGlyph != null && _hitTestedGlyph.Behavior != null) - { - return _hitTestedGlyph.Behavior.OnMouseUp(_hitTestedGlyph, e.Button); - } - return false; } - public void OnPaintGlyphs(PaintEventArgs pe) + if (primary is IComponent comp) { - //Paint any glyphs our tray adorner has - foreach (Glyph g in _traySelectionAdorner.Glyphs) + Control c = TrayControl.FromComponent(comp); + if (c != null && IsHandleCreated) { - g.Paint(pe); + ScrollControlIntoView(c); + UnsafeNativeMethods.NotifyWinEvent((int)AccessibleEvents.Focus, new HandleRef(c, c.Handle), NativeMethods.OBJID_CLIENT, 0); } - } - - public void UpdateLocation(TrayControl trayControl) - { - - foreach (Glyph g in _traySelectionAdorner.Glyphs) + if (glyphManager != null) { - // only look at glyphs that derive from designerglyph base (actions) - if (g is DesignerActionGlyph desGlyph && ((DesignerActionBehavior)(desGlyph.Behavior)).RelatedComponent.Equals(trayControl.Component)) + glyphManager.SelectionGlyphs.Clear(); + IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); + foreach (object selObj in selectedObjects) { - desGlyph.UpdateAlternativeBounds(trayControl.Bounds); + 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); + } + } } } } } - private void OnMenuArrangeIcons(object sender, EventArgs e) + private void OnComponentRemoved(object sender, ComponentEventArgs cevent) + { + RemoveComponent(cevent.Component); + } + + private void OnMenuShowLargeIcons(object sender, EventArgs e) { 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) + t = host.CreateTransaction(SR.TrayShowLargeIcons); + PropertyDescriptor trayIconProp = TypeDescriptor.GetProperties(mainDesigner.Component)["TrayLargeIcon"]; + if (trayIconProp != null) { - trayAAProp.SetValue(mainDesigner.Component, !AutoArrange); + trayIconProp.SetValue(mainDesigner.Component, !ShowLargeIcons); } } finally @@ -357,6 +309,7 @@ private void OnMenuArrangeIcons(object sender, EventArgs e) t.Commit(); } } + private void OnMenuLineupIcons(object sender, EventArgs e) { IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); @@ -372,14 +325,13 @@ private void OnMenuLineupIcons(object sender, EventArgs e) t.Commit(); } } + private void DoLineupIcons() { if (autoArrange) return; - bool oldValue = autoArrange; autoArrange = true; - try { DoAutoArrange(true); @@ -390,18 +342,68 @@ private void DoLineupIcons() } } - private void OnMenuShowLargeIcons(object sender, EventArgs e) + private void DoAutoArrange(bool dirtyDesigner) + { + 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(); + } + } + + private void OnMenuArrangeIcons(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) + t = host.CreateTransaction(SR.TrayAutoArrange); + + PropertyDescriptor trayAAProp = TypeDescriptor.GetProperties(mainDesigner.Component)["TrayAutoArrange"]; + if (trayAAProp != null) { - trayIconProp.SetValue(mainDesigner.Component, !ShowLargeIcons); + trayAAProp.SetValue(mainDesigner.Component, !AutoArrange); } } finally @@ -411,481 +413,252 @@ private void OnMenuShowLargeIcons(object sender, EventArgs e) } } - private void OnComponentRemoved(object sender, ComponentEventArgs cevent) - { - RemoveComponent(cevent.Component); - } - - private void OnSelectionChanged(object sender, EventArgs e) + public bool AutoArrange { - selectedObjects = ((ISelectionService)sender).GetSelectedComponents(); - object primary = ((ISelectionService)sender).PrimarySelection; - Invalidate(); - _fSelectionChanged = true; - - // Accessibility information - foreach (object selObj in selectedObjects) + get => autoArrange; + set { - if (selObj is IComponent component) + if (autoArrange != value) { - Control c = TrayControl.FromComponent(component); - if (c != null) + autoArrange = value; + menucmdArrangeIcons.Checked = value; + + if (autoArrange) { - Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "MSAA: SelectionAdd, traycontrol = " + c.ToString()); - UnsafeNativeMethods.NotifyWinEvent((int)AccessibleEvents.SelectionAdd, new HandleRef(c, c.Handle), NativeMethods.OBJID_CLIENT, 0); + DoAutoArrange(true); } } } + } - if (primary is IComponent comp) + /// + /// Gets the number of compnents contained within this tray. + /// + public int ComponentCount + { + get => Controls.Count; + } + + internal GlyphCollection SelectionGlyphs + { + get { - Control c = TrayControl.FromComponent(comp); - if (c != null && IsHandleCreated) + if (glyphManager != null) { - ScrollControlIntoView(c); - UnsafeNativeMethods.NotifyWinEvent((int)AccessibleEvents.Focus, new HandleRef(c, c.Handle), NativeMethods.OBJID_CLIENT, 0); + return glyphManager.SelectionGlyphs; } - if (glyphManager != null) + else { - 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); - } - } - } + return null; } } } - private void OnComponentRefresh(RefreshEventArgs e) - { - IComponent component = e.ComponentChanged as IComponent; - if (component != null) + /// + /// Determines whether the tray will show large icon view or not. + /// + public bool ShowLargeIcons + { + get => showLargeIcons; + set { - TrayControl control = TrayControl.FromComponent(component); - - if (control != null) + if (showLargeIcons != value) { - 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(); - } + showLargeIcons = value; + menucmdLargeIcons.Checked = ShowLargeIcons; + + ResetTrayControls(); + Invalidate(true); } } } - /// - /// Internally exposes a way for sited components to add glyphs to the component tray. - /// - internal GlyphCollection SelectionGlyphs + bool IExtenderProvider.CanExtend(object extendee) { - get + return (extendee is IComponent comp) && (TrayControl.FromComponent(comp) != null); + } + + IComponent IOleDragClient.Component + { + get => mainDesigner.Component; + } + + bool IOleDragClient.CanModifyComponents + { + get => true; + } + + bool IOleDragClient.AddComponent(IComponent component, string name, bool firstAdd) + { + // the designer for controls decides what to do here + if (mainDesigner is IOleDragClient oleDragClient) { - if (glyphManager != null) + try { - return glyphManager.SelectionGlyphs; + oleDragClient.AddComponent(component, name, firstAdd); + PositionControl(TrayControl.FromComponent(component)); + mouseDropLocation = InvalidPoint; + return true; } - else + 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 { - return null; } } + Debug.Fail("Don't know how to add component!"); + return false; } - private delegate void AsyncInvokeHandler(bool children); + bool IOleDragClient.IsDropOk(IComponent component) => true; - private void OnSystemSettingChanged(object sender, EventArgs e) + Control IOleDragClient.GetDesignerControl() => this; + + Control IOleDragClient.GetControlForComponent(object component) { - if (IsHandleCreated) + if (component is IComponent comp) { - _fResetAmbient = true; - ResetTrayControls(); - BeginInvoke(new AsyncInvokeHandler(Invalidate), new object[] { true }); + return TrayControl.FromComponent(comp); } + Debug.Fail("component is not IComponent"); + return null; } - private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + bool ISelectionUIHandler.BeginDrag(object[] components, SelectionRules rules, int initialX, int initialY) { - if (IsHandleCreated) + if (TabOrderActive) { - _fResetAmbient = true; - ResetTrayControls(); - BeginInvoke(new AsyncInvokeHandler(Invalidate), new object[] { true }); + return false; } - } - private void ResetTrayControls() - { - ControlCollection children = (ControlCollection)Controls; - if (children == null) - return; - - for (int i = 0; i < children.Count; ++i) + bool result = DragHandler.BeginDrag(components, rules, initialX, initialY); + if (result) { - if (children[i] is TrayControl tc) + if (!GetOleDragHandler().DoBeginDrag(components, rules, initialX, initialY)) { - tc._fRecompute = true; + return false; } } + return result; } - public bool AutoArrange + internal virtual OleDragDropHandler GetOleDragHandler() { - get + if (oleDragDropHandler == null) { - return autoArrange; + oleDragDropHandler = new TrayOleDragDropHandler(DragHandler, serviceProvider, this); } + return oleDragDropHandler; + } - set + internal virtual SelectionUIHandler DragHandler + { + get { - if (autoArrange != value) + if (dragHandler == null) { - autoArrange = value; - _menucmdArrangeIcons.Checked = value; - - if (autoArrange) - { - DoAutoArrange(true); - } + dragHandler = new TraySelectionUIHandler(this); } + return dragHandler; } } - private void DoAutoArrange(bool dirtyDesigner) - { - if (controls == null || controls.Count <= 0) - { - return; - } - controls.Sort(new AutoArrangeComparer()); - SuspendLayout(); + void ISelectionUIHandler.DragMoved(object[] components, Rectangle offset) => DragHandler.DragMoved(components, offset); - //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 + 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) { - Control prevCtl = null; - bool positionedGlobal = true; - foreach (Control ctl in controls) + foreach (IComponent comp in components) { - 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. - //his 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) + TrayControl tc = TrayControl.FromComponent(comp); + if (tc != null) { - PositionInNextAutoSlot(ctl as TrayControl, prevCtl, false); - positionedGlobal = false; + SetTrayLocation(comp, new Point(tc.Location.X - autoScrollPosBeforeDragging.X, tc.Location.Y - autoScrollPosBeforeDragging.Y)); } - prevCtl = ctl; - } - - if (selectionUISvc != null) - { - selectionUISvc.SyncSelection(); } - } - finally - { - ResumeLayout(); + AutoScrollPosition = new Point(-autoScrollPosBeforeDragging.X, -autoScrollPosBeforeDragging.Y); } } - private bool PositionInNextAutoSlot(TrayControl c, Control prevCtl, bool dirtyDesigner) - { - Debug.Assert(c.Visible, "TrayControl for " + c.Component + " should not be positioned"); - if (whiteSpace.IsEmpty) + // 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) { - 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; + return RectangleToScreen(ClientRectangle); } + return Rectangle.Empty; + } - 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 = this.AutoScrollPosition; - newLoc = new Point(newLoc.X - autoScrollLoc.X, newLoc.Y - autoScrollLoc.Y); - ctlLocation.SetValue(comp, newLoc); - } - } - else - { - c.Location = newLoc; - } - return true; - } - } - return false; - } - - /// - /// Gets the number of compnents contained within this tray. - /// - public int ComponentCount - { - get => Controls.Count; - } - - /// - /// Determines whether the tray will show large icon view or not. - /// - public bool ShowLargeIcons - { - get => showLargeIcons; - set - { - if (showLargeIcons != value) - { - showLargeIcons = value; - _menucmdLargeIcons.Checked = ShowLargeIcons; - ResetTrayControls(); - Invalidate(true); - } - } - } - - private bool TabOrderActive + void ISelectionUIHandler.OnSelectionDoubleClick(IComponent component) { - get + if (!TabOrderActive) { - if (!queriedTabOrder) - { - queriedTabOrder = true; - IMenuCommandService mcs = MenuService; - if (mcs != null) - { - tabOrderCommand = mcs.FindCommand(MenuCommands.TabOrder); - } - } - - if (tabOrderCommand != null) + if (((IOleDragClient)this).GetControlForComponent(component) is TrayControl tc) { - return tabOrderCommand.Checked; + tc.ViewDefaultEvent(component); } - return false; } } - bool IExtenderProvider.CanExtend(object extendee) - { - return (extendee is IComponent comp) && (TrayControl.FromComponent(comp) != null); - } - - IComponent IOleDragClient.Component - { - get => mainDesigner.Component; - } - - bool IOleDragClient.CanModifyComponents + bool ISelectionUIHandler.QueryBeginDrag(object[] components, SelectionRules rules, int initialX, int initialY) => DragHandler.QueryBeginDrag(components, rules, initialX, initialY); + + void ISelectionUIHandler.ShowContextMenu(IComponent component) { - get => true; + Point cur = Control.MousePosition; + OnContextMenu(cur.X, cur.Y, true); } - bool IOleDragClient.AddComponent(IComponent component, string name, bool firstAdd) + private void OnContextMenu(int x, int y, bool useSelection) { - // 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 + if (!TabOrderActive) { - // for webforms (98109) just add the component directly to the host - IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); - try + Capture = false; + IMenuCommandService mcs = MenuService; + if (mcs != null) { - if (host != null && host.Container != null) + Capture = false; + Cursor.Clip = Rectangle.Empty; + ISelectionService s = (ISelectionService)GetService(typeof(ISelectionService)); + if (useSelection && s != null && !(1 == s.SelectionCount && s.PrimarySelection == mainDesigner.Component)) { - if (host.Container.Components[name] != null) - { - name = null; - } - host.Container.Add(component, name); - return true; + mcs.ShowContextMenu(MenuCommands.TraySelectionMenu, x, y); } - } - catch - { - } - - } - Debug.Fail("Don't know how to add component!"); - return false; - } - - bool IOleDragClient.IsDropOk(IComponent component) - { - return true; - } - - Control IOleDragClient.GetDesignerControl() - { - return this; - } - - Control IOleDragClient.GetControlForComponent(object component) - { - IComponent comp = component as IComponent; - if (comp != null) - { - return TrayControl.FromComponent(comp); - } - Debug.Fail("component is not IComponent"); - return null; - } - - bool ISelectionUIHandler.BeginDrag(object[] components, SelectionRules rules, int initialX, int initialY) - { - 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.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) + else { - SetTrayLocation(comp, new Point(tc.Location.X - autoScrollPosBeforeDragging.X, tc.Location.Y - autoScrollPosBeforeDragging.Y)); + mcs.ShowContextMenu(MenuCommands.ComponentTrayMenu, x, y); } } - AutoScrollPosition = new Point(-autoScrollPosBeforeDragging.X, -autoScrollPosBeforeDragging.Y); - } - } - - Rectangle ISelectionUIHandler.GetComponentBounds(object component) - { - // We render the selection UI glyph ourselves. - return 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); - } - void ISelectionUIHandler.OleDragEnter(DragEventArgs de) => GetOleDragHandler().DoOleDragEnter(de); - + void ISelectionUIHandler.OleDragDrop(DragEventArgs de) => GetOleDragHandler().DoOleDragDrop(de); void ISelectionUIHandler.OleDragOver(DragEventArgs de) => GetOleDragHandler().DoOleDragOver(de); @@ -893,7 +666,7 @@ void ISelectionUIHandler.ShowContextMenu(IComponent component) void ISelectionUIHandler.OleDragLeave() => GetOleDragHandler().DoOleDragLeave(); /// - /// Adds a component to the tray. + /// Adds a component to the tray. /// public virtual void AddComponent(IComponent component) { @@ -903,11 +676,11 @@ public virtual void AddComponent(IComponent 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) { @@ -922,17 +695,16 @@ public virtual void AddComponent(IComponent component) SuspendLayout(); try { - // Add it to us + // 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: + // 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/ + // 6. This causes all sorts of badness // Fix is to refresh. TypeDescriptor.Refresh(component); if (host != null && !host.Loading) @@ -943,7 +715,6 @@ public virtual void AddComponent(IComponent component) { selectionUISvc.AssignSelectionUIHandler(component, this); } - InheritanceAttribute attr = trayctl.InheritanceAttribute; if (attr.InheritanceLevel != InheritanceLevel.NotInherited) { @@ -958,7 +729,6 @@ public virtual void AddComponent(IComponent component) { ResumeLayout(); } - if (host != null && !host.Loading) { ScrollControlIntoView(trayctl); @@ -978,13 +748,8 @@ protected virtual bool CanCreateComponentFromTool(ToolboxItem tool) { return true; } - Type designerType = GetDesignerType(compType, typeof(IDesigner)); - if (typeof(ControlDesigner).IsAssignableFrom(designerType)) - { - return false; - } - return true; + return ! (typeof(ControlDesigner).IsAssignableFrom(designerType)); } private Type GetDesignerType(Type t, Type designerBaseType) @@ -1024,8 +789,9 @@ private Type GetDesignerType(Type t, Type designerBaseType) /// /// 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. + /// 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) { @@ -1065,15 +831,14 @@ protected void DisplayError(Exception e) } /// - /// 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) { 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); @@ -1092,22 +857,21 @@ protected override void Dispose(bool disposing) IComponentChangeService componentChangeService = (IComponentChangeService)GetService(typeof(IComponentChangeService)); if (componentChangeService != null) { - componentChangeService.ComponentRemoved -= new ComponentEventHandler(this.OnComponentRemoved); + componentChangeService.ComponentRemoved -= new ComponentEventHandler(OnComponentRemoved); } - TypeDescriptor.Refreshed -= new RefreshEventHandler(OnComponentRefresh); - SystemEvents.DisplaySettingsChanged -= new EventHandler(this.OnSystemSettingChanged); - SystemEvents.InstalledFontsChanged -= new EventHandler(this.OnSystemSettingChanged); - SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged); + 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); + 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) @@ -1120,6 +884,7 @@ protected override void Dispose(bool disposing) } } selectionUISvc = null; + if (inheritanceUI != null) { inheritanceUI.Dispose(); @@ -1129,6 +894,7 @@ protected override void Dispose(bool disposing) serviceProvider = null; controls.Clear(); controls = null; + if (glyphManager != null) { glyphManager.Dispose(); @@ -1139,8 +905,10 @@ protected override void Dispose(bool 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) { @@ -1168,7 +936,8 @@ public IComponent GetNextComponent(IComponent component, bool forward) } /// - /// 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)] @@ -1179,7 +948,6 @@ public IComponent GetNextComponent(IComponent component, bool forward) [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] public Point GetLocation(IComponent receiver) { - // We shouldn't really end up here, but if we do.... PropertyDescriptor loc = TypeDescriptor.GetProperties(receiver.GetType())["Location"]; if (loc != null) { @@ -1194,7 +962,8 @@ public Point GetLocation(IComponent 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)] @@ -1209,14 +978,13 @@ public Point GetTrayLocation(IComponent receiver) Debug.Fail("Anything we're extending should have a component view."); return new Point(); } - Point loc = c.Location; - Point autoScrollLoc = this.AutoScrollPosition; + 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) { @@ -1229,104 +997,6 @@ protected override object GetService(Type serviceType) return service; } - /// - /// Returns the traycontrol representing the IComponent. If no traycontrol is found, this returns null. This is used identify bounds for the DesignerAction UI. - /// - internal TrayControl GetTrayControlFromComponent(IComponent comp) - { - return TrayControl.FromComponent(comp); - } - - /// - /// Called from CommandSet's OnMenuPaste method. This will allow us to properly adjust the location of the components in the tray after we've incorreclty set them by deserializing the design time properties (and hence called SetValue(c, myBadLocation) on the location property). - /// - 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 // DEBUG - TrayControl tc = (TrayControl)c; - tc.Positioned = true; - tc.Location = pos; - } - /// /// Returns true if the given componenent is being shown on the tray. /// @@ -1336,8 +1006,7 @@ public bool IsTrayComponent(IComponent comp) { return false; } - - foreach (Control control in this.Controls) + foreach (Control control in Controls) { if (control is TrayControl tc && tc.Component == comp) { @@ -1355,14 +1024,12 @@ protected override void OnMouseDoubleClick(MouseEventArgs 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"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (eps != null), "IEventBindingService not found"); if (eps != null) { eps.ShowCode(); @@ -1380,146 +1047,20 @@ protected override void OnGiveFeedback(GiveFeedbackEventArgs gfevent) GetOleDragHandler().DoOleGiveFeedback(gfevent); } - internal virtual OleDragDropHandler GetOleDragHandler() - { - if (oleDragDropHandler == null) - { - oleDragDropHandler = new TrayOleDragDropHandler(this.DragHandler, this.serviceProvider, this); - } - return oleDragDropHandler; - } - - internal virtual SelectionUIHandler DragHandler - { - get - { - if (dragHandler == null) - { - dragHandler = new TraySelectionUIHandler(this); - } - return dragHandler; - } - } - - 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; - } - } - - private class TraySelectionUIHandler : SelectionUIHandler - { - private ComponentTray _tray; - private Size _snapSize = Size.Empty; - - public TraySelectionUIHandler(ComponentTray tray) - { - this._tray = tray; - _snapSize = new Size(); - } - - public override bool BeginDrag(object[] components, SelectionRules rules, int initialX, int initialY) - { - bool value = base.BeginDrag(components, rules, initialX, initialY); - _tray.SuspendLayout(); - return value; - } - - public override void EndDrag(object[] components, bool cancel) - { - base.EndDrag(components, cancel); - _tray.ResumeLayout(); - } - - protected override IComponent GetComponent() => _tray; - - protected override Control GetControl() => _tray; - - protected override Control GetControl(IComponent component) => TrayControl.FromComponent(component); - - protected override Size GetCurrentSnapSize() => _snapSize; - - protected override object GetService(Type serviceType) => _tray.GetService(serviceType); - - protected override bool GetShouldSnapToGrid() => false; - - public override Rectangle GetUpdatedRect(Rectangle originalRect, Rectangle dragRect, bool updateSize) => dragRect; - - public override void SetCursor() => _tray.OnSetCursor(); - } - /// - /// 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) { - // 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. + // 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 = this.AutoScrollPosition;//save the scroll position + 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"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (GetService(typeof(IDesignerHost)) != null), "IDesignerHost not found"); try { IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); @@ -1563,7 +1104,6 @@ protected override void OnDragEnter(DragEventArgs de) { toolboxService = (IToolboxService)GetService(typeof(IToolboxService)); } - OleDragDropHandler dragDropHandler = GetOleDragHandler(); object[] dragComps = dragDropHandler.GetDraggingObjects(de); // Only assume the items came from the ToolBox if dragComps == null @@ -1571,7 +1111,6 @@ protected override void OnDragEnter(DragEventArgs de) { 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?"); @@ -1617,6 +1156,7 @@ protected override void OnDragOver(DragEventArgs de) } } + /// /// /// Forces the layout of any docked or anchored child controls. /// @@ -1629,8 +1169,9 @@ protected override void OnLayout(LayoutEventArgs 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() { @@ -1668,6 +1209,7 @@ protected override void OnMouseDown(MouseEventArgs e) //handled by a glyph - so don't send to the comp tray return; } + base.OnMouseDown(e); if (!TabOrderActive) { @@ -1675,7 +1217,6 @@ protected override void OnMouseDown(MouseEventArgs e) { toolboxService = (IToolboxService)GetService(typeof(IToolboxService)); } - FocusDesigner(); if (e.Button == MouseButtons.Left && toolboxService != null) { @@ -1714,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 }); @@ -1729,6 +1269,7 @@ protected override void OnMouseDown(MouseEventArgs e) throw; } } + } } } @@ -1745,10 +1286,9 @@ protected override void OnMouseMove(MouseEventArgs 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) @@ -1777,19 +1317,22 @@ protected override void OnMouseUp(MouseEventArgs e) //handled by a glyph - so don't send to the comp tray return; } + if (mouseDragStart != InvalidPoint && e.Button == MouseButtons.Left) { - object[] comps = null; + object[] comps; Capture = false; Cursor.Clip = Rectangle.Empty; if (mouseDragEnd != InvalidPoint) { DrawRubber(mouseDragStart, mouseDragEnd); - Rectangle rect = new Rectangle(); - rect.X = Math.Min(mouseDragStart.X, e.X); - rect.Y = Math.Min(mouseDragStart.Y, e.Y); - rect.Width = Math.Abs(e.X - mouseDragStart.X); - rect.Height = Math.Abs(e.Y - mouseDragStart.Y); + 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; } @@ -1802,12 +1345,10 @@ protected override void OnMouseUp(MouseEventArgs e) { comps = new object[] { mainDesigner.Component }; } - 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); @@ -1825,6 +1366,7 @@ protected override void OnMouseUp(MouseEventArgs e) } base.OnMouseUp(e); } + private object[] GetComponentsInRect(Rectangle rect) { ArrayList list = new ArrayList(); @@ -1843,10 +1385,10 @@ private object[] GetComponentsInRect(Rectangle rect) protected override void OnPaint(PaintEventArgs pe) { - if (_fResetAmbient || _fSelectionChanged) + if (fResetAmbient || fSelectionChanged) { - _fResetAmbient = false; - _fSelectionChanged = false; + fResetAmbient = false; + fSelectionChanged = false; IUIService uiService = (IUIService)GetService(typeof(IUIService)); if (uiService != null) { @@ -1873,7 +1415,6 @@ protected override void OnPaint(PaintEventArgs pe) BackColor = styleColor; Font = (Font)uiService.Styles["DialogFont"]; - foreach (Control ctl in controls) { ctl.BackColor = styleColor; @@ -1911,14 +1452,11 @@ protected override void OnPaint(PaintEventArgs pe) 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); } @@ -1940,34 +1478,9 @@ protected override void OnPaint(PaintEventArgs pe) } } - internal class NoResizeHandleGlyph : SelectionGlyphBase - { - private bool _isPrimary = false; - internal NoResizeHandleGlyph(Rectangle controlBounds, SelectionRules selRules, bool primarySelection, Behavior.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; - - } - public override void Paint(PaintEventArgs pe) - { - DesignerUtils.DrawNoResizeHandle(pe.Graphics, bounds, _isPrimary, this); - } - - } - /// - /// 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() { @@ -1975,7 +1488,6 @@ protected virtual void OnSetCursor() { toolboxService = (IToolboxService)GetService(typeof(IToolboxService)); } - if (toolboxService == null || !toolboxService.SetCursor()) { Cursor.Current = Cursors.Default; @@ -1997,7 +1509,6 @@ public virtual void RemoveComponent(IComponent component) { inheritanceUI.RemoveInheritedControl(c); } - if (controls != null) { int index = controls.IndexOf(c); @@ -2013,7 +1524,8 @@ public virtual void RemoveComponent(IComponent component) } /// - /// 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) { @@ -2042,7 +1554,8 @@ public void SetLocation(IComponent receiver, Point 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) { @@ -2052,12 +1565,10 @@ public void SetTrayLocation(IComponent receiver, Point location) Debug.Fail("Anything we're extending should have a component view."); return; } - if (c.Parent == this) { - Point autoScrollLoc = this.AutoScrollPosition; + Point autoScrollLoc = AutoScrollPosition; location = new Point(location.X + autoScrollLoc.X, location.Y + autoScrollLoc.Y); - if (c.Visible) { RearrangeInAutoSlots(c, location); @@ -2093,7 +1604,6 @@ protected override void WndProc(ref Message m) selectionUISvc.SyncSelection(); } return; - case NativeMethods.WM_STYLECHANGED: // When the scroll bars first appear, we need to invalidate so we properly paint our grid. Invalidate(); @@ -2133,26 +1643,53 @@ protected override void WndProc(ref Message m) } } - private void OnContextMenu(int x, int y, bool useSelection) + internal TrayControl GetTrayControlFromComponent(IComponent comp) { - 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 + return TrayControl.FromComponent(comp); + } + + private bool TabOrderActive + { + get + { + if (!queriedTabOrder) + { + queriedTabOrder = true; + IMenuCommandService mcs = MenuService; + if (mcs != null) { - mcs.ShowContextMenu(MenuCommands.ComponentTrayMenu, x, y); + 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; } } @@ -2174,38 +1711,181 @@ internal void FocusDesigner() } } } - internal class AutoArrangeComparer : IComparer + + internal Size ParentGridSize { - int IComparer.Compare(object o1, object o2) + get { - Debug.Assert(o1 != null && o2 != null, "Null objects sent for comparison!!!"); + if (mainDesigner is ParentControlDesigner designer) + { + return designer.ParentGridSize; + } - Point tcLoc1 = ((Control)o1).Location; - Point tcLoc2 = ((Control)o2).Location; - int height = ((Control)o1).Height / 2; + return new Size(8, 8); + } + } - // If they are at the same location, they are equal. - if (tcLoc1.X == tcLoc2.X && tcLoc1.Y == tcLoc2.Y) + internal void UpdatePastePositions(ArrayList components) + { + foreach (TrayControl c in components) + { + if (!CanDisplayComponent(c.Component)) { - return 0; + return; } - // Is the first control lower than the 2nd... - if (tcLoc1.Y + height <= tcLoc2.Y) - return -1; + 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(); + } + } - // Is the 2nd control lower than the first... - if (tcLoc2.Y + height <= tcLoc1.Y) - return 1; + 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); + } + } + } - // Which control is left of the other... - return ((tcLoc1.X <= tcLoc2.X) ? -1 : 1); + 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; } - /// - /// The tray control is the UI we show for each component in the tray. - /// internal class TrayControl : Control { // Values that define this tray control @@ -2213,21 +1893,17 @@ internal class TrayControl : Control 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. /// @@ -2237,16 +1913,13 @@ 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"); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (cs != null), "IComponentChangeService not found"); if (cs != null) { cs.ComponentRename += new ComponentRenameEventHandler(OnComponentRename); @@ -2258,7 +1931,6 @@ public TrayControl(ComponentTray tray, IComponent component) 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) @@ -2269,12 +1941,14 @@ public TrayControl(ComponentTray tray, IComponent component) 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. + // 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; + + } /// @@ -2304,13 +1978,11 @@ public bool Positioned /// /// 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. + // 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)); @@ -2346,75 +2018,33 @@ private void AdjustSize() } } - protected override AccessibleObject CreateAccessibilityInstance() - { - return new TrayControlAccessibleObject(this, _tray); - } + protected override AccessibleObject CreateAccessibilityInstance() => new TrayControlAccessibleObject(this, _tray); - private class TrayControlAccessibleObject : ControlAccessibleObject + /// + /// Destroys this control. Views automatically destroy themselves when they are removed from the design container. + /// + protected override void Dispose(bool disposing) { - 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; - } - } - } - - /// - /// Destroys this control. Views automatically destroy themselves when they are removed from the design container. - /// - protected override void Dispose(bool disposing) - { - if (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"); + 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); } } } - base.Dispose(disposing); } @@ -2424,7 +2054,6 @@ protected override void Dispose(bool disposing) public static TrayControl FromComponent(IComponent component) { TrayControl c = null; - if (component == null) { return null; @@ -2434,14 +2063,12 @@ 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)); } } - return c; } @@ -2467,12 +2094,11 @@ protected override void OnHandleCreated(EventArgs e) } /// - /// Called in response to a double-click of the left mouse button. The default behavior here calls onDoubleClick on IMouseHandler + /// 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)); @@ -2480,12 +2106,9 @@ protected override void OnDoubleClick(EventArgs e) 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); @@ -2498,74 +2121,12 @@ protected override void OnDoubleClick(EventArgs e) } } - /// - /// 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(); - } - } - } - /// /// Terminates our drag operation. /// private void OnEndDrag(bool cancel) { _mouseDragLast = InvalidPoint; - if (!_mouseDragMoved) { if (_ctrlSelect) @@ -2581,7 +2142,6 @@ private void OnEndDrag(bool cancel) } _mouseDragMoved = false; _ctrlSelect = false; - Capture = false; OnSetCursor(); @@ -2599,30 +2159,21 @@ private void OnEndDrag(bool cancel) 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 + // 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"); - + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (sel != null), "ISelectionService not found"); if (sel != null) { sel.SetSelectedComponents(new object[] { Component }, SelectionTypes.Primary); @@ -2649,7 +2200,6 @@ protected override void OnMouseMove(MouseEventArgs me) 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 || @@ -2689,8 +2239,7 @@ protected override void OnMouseMove(MouseEventArgs me) } /// - /// Called when the mouse button is released. Here, we finish our drag - /// if one was started. + /// Called when the mouse button is released. Here, we finish our drag if one was started. /// protected override void OnMouseUp(MouseEventArgs me) { @@ -2706,15 +2255,12 @@ 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) { @@ -2735,18 +2281,14 @@ protected override void OnPaint(PaintEventArgs e) _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; @@ -2770,7 +2312,6 @@ protected override void OnPaint(PaintEventArgs e) 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; @@ -2801,30 +2342,6 @@ protected override void OnPaint(PaintEventArgs e) } } - 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(); - } - /// /// Overrides control's FontChanged. Here we re-adjust our size if the font changes. /// @@ -2854,6 +2371,12 @@ protected override void OnTextChanged(EventArgs e) 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. @@ -2864,8 +2387,7 @@ private void OnSetCursor() } 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. + // 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; @@ -2897,6 +2419,532 @@ private void OnSetCursor() 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)); + Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || (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/ContainerSelectorActiveEventHandler.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ContainerSelectorActiveEventHandler.cs index 8dd04840e20..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 @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. [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 { /// @@ -11,3 +10,4 @@ namespace System.Windows.Forms.Design /// 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 3c8844f8c2f..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 @@ -2,102 +2,290 @@ // 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.Globalization; 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 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 + // 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 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) + { + _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); + protected AccessibleObject accessibilityObj = null; - /// - /// Retrieves the control we're designing. - /// - public virtual Control Control => throw new NotImplementedException(SR.NotImplementedByDesign); + public virtual AccessibleObject AccessibilityObject + { + get + { + if (accessibilityObj == null) + { + accessibilityObj = new ControlDesignerAccessibleObject(this, Control); + } + return accessibilityObj; + } + } /// - /// Determines whether drag rects can be drawn on this designer. + /// Retrieves the control we're designing. /// - protected virtual bool EnableDragRect => throw new NotImplementedException(SR.NotImplementedByDesign); + public virtual Control Control + { + get => (Control)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. + /// Determines whether drag rects can be drawn on this designer. /// - protected override IComponent ParentComponent => throw new NotImplementedException(SR.NotImplementedByDesign); + protected virtual bool EnableDragRect + { + get => false; + } /// - /// 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. + /// 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. /// - public virtual bool ParticipatesWithSnapLines => 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. /// + 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; + } + + 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 => throw new NotImplementedException(SR.NotImplementedByDesign); + public virtual SelectionRules SelectionRules + { + get + { + object component = Component; + SelectionRules 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 && 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; + } + } - // 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 virtual bool ControlSupportsSnaplines { get => true; } - /// Used when adding 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. 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) @@ -107,509 +295,2859 @@ internal Point GetOffsetToClientArea() return (new Point(Math.Abs(nativeOffset.x - offset.X), nativeOffset.y - offset.Y)); } - /// - /// 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); + 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); + } - protected override InheritanceAttribute InheritanceAttribute => - throw new NotImplementedException(SR.NotImplementedByDesign); + if (autoSizeModeProp != null) + { + AutoSizeMode mode = (AutoSizeMode)autoSizeModeProp.GetValue(component); + growOnly = mode == AutoSizeMode.GrowOnly; + } - /// - /// 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); + if (autoSize) + { + resizable = growOnly; + } + return resizable; } /// - /// 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 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 ControlDesigner InternalControlDesigner(int internalControlIndex) + public virtual IList SnapLines { - throw new NotImplementedException(SR.NotImplementedByDesign); + get => SnapLinesInternal(); } - /// - /// 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) + internal IList SnapLinesInternal() { - throw new NotImplementedException(SR.NotImplementedByDesign); + return SnapLinesInternal(Control.Margin); } - /// - /// 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) + internal IList SnapLinesInternal(Padding margin) { - ParentControlDesigner p = parentDesigner as ParentControlDesigner; - return p != null && !Control.Contains(p.Control); + 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; } - /// - /// 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) + protected override InheritanceAttribute InheritanceAttribute { - throw new NotImplementedException(SR.NotImplementedByDesign); + get + { + if (IsRootDesigner) + { + return InheritanceAttribute.Inherited; + } + return base.InheritanceAttribute; + } } - /// - /// Displays the given exception to the user. - /// - protected void DisplayError(Exception e) + internal new bool IsRootDesigner { - throw new NotImplementedException(SR.NotImplementedByDesign); + 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; + } } /// - /// Disposes of this object. + /// 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. /// - protected override void Dispose(bool disposing) - { - throw new NotImplementedException(SR.NotImplementedByDesign); - } + public virtual int NumberOfInternalControlDesigners() => 0; /// - /// 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. + /// 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. /// - protected bool EnableDesignMode(Control child, string name) - { - throw new NotImplementedException(SR.NotImplementedByDesign); - } + public virtual ControlDesigner InternalControlDesigner(int internalControlIndex) => null; /// - /// Enables or disables drag/drop support. This - /// hooks drag event handlers to the control. + /// 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 EnableDragDrop(bool value) - { - throw new NotImplementedException(SR.NotImplementedByDesign); - } + protected void BaseWndProc(ref Message m) => m.Result = UnsafeNativeMethods.DefWindowProc(m.HWnd, m.Msg, m.WParam, m.LParam); /// - /// 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. + /// 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. /// - protected virtual ControlBodyGlyph GetControlGlyph(GlyphSelectionType selectionType) - { - throw new NotImplementedException(SR.NotImplementedByDesign); - } - - internal ControlBodyGlyph GetControlGlyphInternal(GlyphSelectionType selectionType) - { - return GetControlGlyph(selectionType); - } + public virtual bool CanBeParentedTo(IDesigner parentDesigner) => parentDesigner is ParentControlDesigner p && !Control.Contains(p.Control); /// - /// 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. + /// Default processing for messages. This method causes the message to get processed by the control, rather than the designer. /// - public virtual GlyphCollection GetGlyphs(GlyphSelectionType selectionType) + protected void DefWndProc(ref Message m) { - throw new NotImplementedException(SR.NotImplementedByDesign); + _designerTarget.DefWndProc(ref m); } /// - /// 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. + /// Displays the given exception to the user. /// - protected virtual bool GetHitTest(Point point) + protected void DisplayError(Exception e) { - return false; + 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); + } } /// - /// 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. + /// Disposes of this object. /// - protected void HookChildControls(Control firstChild) + 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(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); } - /// - /// Called by the host when we're first initialized. - /// - public override void Initialize(IComponent component) + private void OnControlAdded(object sender, ControlEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + 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(); + 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); + } + } + } } - /// - /// ControlDesigner overrides this method to handle after-drop cases. - /// - public override void InitializeExistingComponent(IDictionary defaultValues) + private interface IDesignerTarget : IDisposable { - throw new NotImplementedException(SR.NotImplementedByDesign); + void DefWndProc(ref Message m); } - /// - /// 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) + private void DetachContextMenu(object sender, EventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + ContextMenu = null; } - /// - /// 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() + private ContextMenu ContextMenu { - throw new NotImplementedException(SR.NotImplementedByDesign); - } + get => (ContextMenu)ShadowProperties["ContextMenu"]; + set + { + ContextMenu oldValue = (ContextMenu)ShadowProperties["ContextMenu"]; - /// - /// Called when the context menu should be displayed - /// - protected virtual void OnContextMenu(int x, int y) - { - ShowContextMenu(x, y); - } + if (oldValue != value) + { + EventHandler disposedHandler = new EventHandler(DetachContextMenu); - /// - /// This is called immediately after the control handle has been created. - /// - protected virtual void OnCreateHandle() - { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (oldValue != null) + { + oldValue.Disposed -= disposedHandler; + } + + ShadowProperties["ContextMenu"] = value; + + if (value != null) + { + value.Disposed += disposedHandler; + } + } + + } } - /// - /// Called when a drag-drop operation enters the control designer view - /// - protected virtual void OnDragEnter(DragEventArgs de) + private void DataSource_ComponentRemoved(object sender, ComponentEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + // 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; + } } /// - /// Called to cleanup a D&D operation + /// 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 virtual void OnDragComplete(DragEventArgs de) + protected bool EnableDesignMode(Control child, string name) { - // default implementation - does nothing. + 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; } /// - /// Called when a drag drop object is dropped onto the control designer view + /// Enables or disables drag/drop support. This hooks drag event handlers to the control. /// - protected virtual void OnDragDrop(DragEventArgs de) + protected void EnableDragDrop(bool value) { - throw new NotImplementedException(SR.NotImplementedByDesign); + Control rc = Control; + if (rc == null) + { + return; + } + + if (value) + { + 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; + } + else + { + 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; + } } - /// - /// Called when a drag-drop operation leaves the control designer view - /// - protected virtual void OnDragLeave(EventArgs e) + private void OnGiveFeedback(object s, GiveFeedbackEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + OnGiveFeedback(e); } - /// - /// Called when a drag drop object is dragged over the control designer view - /// - protected virtual void OnDragOver(DragEventArgs de) + private void OnDragLeave(object s, EventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + OnDragLeave(e); } - /// - /// 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) + private void OnDragEnter(object s, DragEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (BehaviorService != null) + { + //Tell the BehaviorService to monitor mouse messages so it can send appropriate drag notifications. + BehaviorService.StartDragNotification(); + } + OnDragEnter(e); } - /// - /// 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) + private void OnDragOver(object s, DragEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + OnDragOver(e); } - /// - /// Called at the end of a drag operation. This either commits or rolls back the - /// drag. - /// - protected virtual void OnMouseDragEnd(bool cancel) + private void OnDragDrop(object s, DragEventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + if (BehaviorService != null) + { + //this will cause the BehSvc to return from 'drag mode' + BehaviorService.EndDragNotification(); + } + OnDragDrop(e); } - /// - /// 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) + internal System.Windows.Forms.Design.Behavior.Behavior MoveBehavior { - throw new NotImplementedException(SR.NotImplementedByDesign); + get + { + if (_moveBehavior == null) + { + _moveBehavior = new ContainerSelectorBehavior(Control, Component.Site); + } + return _moveBehavior; + } } /// - /// Called when the mouse first enters the control. This is forwarded to the parent - /// designer to enable the container selector. + /// 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 void OnMouseEnter() + 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; } + internal ControlBodyGlyph GetControlGlyphInternal(GlyphSelectionType selectionType) => GetControlGlyph(selectionType); + /// - /// Called after the mouse hovers over the control. This is forwarded to the parent - /// designer to enabled the container selector. + /// 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. /// - protected virtual void OnMouseHover() + 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; } - /// - /// Called when the mouse first enters the control. This is forwarded to the parent - /// designer to enable the container selector. - /// - protected virtual void OnMouseLeave() + internal virtual Behavior.Behavior StandardBehavior { - throw new NotImplementedException(SR.NotImplementedByDesign); + get + { + if (_resizeBehavior == null) + { + _resizeBehavior = new ResizeBehavior(Component.Site); + } + return _resizeBehavior; + } } - /// - /// 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) + internal virtual bool SerializePerformLayout { - throw new NotImplementedException(SR.NotImplementedByDesign); + get => false; } /// - /// 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. + /// 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 void OnSetCursor() + protected virtual bool GetHitTest(Point point) { - throw new NotImplementedException(SR.NotImplementedByDesign); + return false; } /// - /// 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. + /// 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 override void PreFilterProperties(IDictionary properties) + 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(); + UnsafeNativeMethods.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); + } + } + } } - /// - /// 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) + private void OnChildHandleCreated(object sender, EventArgs e) { - throw new NotImplementedException(SR.NotImplementedByDesign); + Control child = sender as Control; + + Debug.Assert(child != null); + + if (child != null) + { + Debug.Assert(child.IsHandleCreated); + HookChildHandles(child.Handle); + } } /// - /// 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. + /// Called by the host when we're first initialized. /// - protected virtual void WndProc(ref Message m) - { - throw new NotImplementedException(SR.NotImplementedByDesign); - } - - [ComVisible(true)] - public class ControlDesignerAccessibleObject : AccessibleObject + public override void Initialize(IComponent component) { - public ControlDesignerAccessibleObject(ControlDesigner designer, Control control) + // 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)) { - throw new NotImplementedException(SR.NotImplementedByDesign); + Visible = true; + } + else + { + Visible = (bool)visibleProp.GetValue(component); } - public override Rectangle Bounds => throw new NotImplementedException(SR.NotImplementedByDesign); - - public override string Description => throw new NotImplementedException(SR.NotImplementedByDesign); - - public override string DefaultAction => throw new NotImplementedException(SR.NotImplementedByDesign); - - public override string Name => throw new NotImplementedException(SR.NotImplementedByDesign); + PropertyDescriptor enabledProp = props["Enabled"]; + if (enabledProp == null || enabledProp.PropertyType != typeof(bool) || !enabledProp.ShouldSerializeValue(component)) + { + Enabled = true; + } + else + { + Enabled = (bool)enabledProp.GetValue(component); + } + base.Initialize(component); + // 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; - public override AccessibleObject Parent => throw new NotImplementedException(SR.NotImplementedByDesign); + Control.ControlAdded += new ControlEventHandler(OnControlAdded); + Control.ControlRemoved += new ControlEventHandler(OnControlRemoved); + Control.ParentChanged += new EventHandler(OnParentChanged); - public override AccessibleRole Role => throw new NotImplementedException(SR.NotImplementedByDesign); + Control.SizeChanged += new EventHandler(OnSizeChanged); + Control.LocationChanged += new EventHandler(OnLocationChanged); - public override AccessibleStates State => throw new NotImplementedException(SR.NotImplementedByDesign); + // Replace the control's window target with our own. This allows us to hook messages. + DesignerTarget = new DesignerWindowTarget(this); - public override string Value => throw new NotImplementedException(SR.NotImplementedByDesign); + // If the handle has already been created for this control, invoke OnCreateHandle so we can hookup our child control subclass. + if (Control.IsHandleCreated) + { + OnCreateHandle(); + } - public override AccessibleObject GetChild(int index) + // If we are an inherited control, notify our inheritance UI + if (Inherited && _host != null && _host.RootComponent != component) { - throw new NotImplementedException(SR.NotImplementedByDesign); + _inheritanceUI = (InheritanceUI)GetService(typeof(InheritanceUI)); + if (_inheritanceUI != null) + { + _inheritanceUI.AddInheritedControl(Control, InheritanceAttribute.InheritanceLevel); + } } - public override int GetChildCount() + // 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) { - throw new NotImplementedException(SR.NotImplementedByDesign); + Control.Visible = true; } - public override AccessibleObject GetFocused() + // 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); + } + + // 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) { - throw new NotImplementedException(SR.NotImplementedByDesign); + cache.RemoveEntry(component); } + } - public override AccessibleObject GetSelected() + 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) { - throw new NotImplementedException(SR.NotImplementedByDesign); + cache.RemoveEntry(component); } + } - public override AccessibleObject HitTest(int x, int y) + private void OnParentChanged(object sender, EventArgs e) + { + if (Control.IsHandleCreated) { - throw new NotImplementedException(SR.NotImplementedByDesign); + OnHandleChange(); } } - /// - /// 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 void OnControlRemoved(object sender, ControlEventArgs e) { - /// - /// Constructor that accepts the related ControlDesigner. - /// - internal TransparentBehavior(ControlDesigner designer) + if (e.Control != null) { - throw new NotImplementedException(SR.NotImplementedByDesign); + // 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); } + } - /// - /// 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) + private void DataBindingsCollectionChanged(object sender, CollectionChangeEventArgs e) + { + // It is possible to use the control designer with NON CONTROl types. + if (Component is Control ctl) { - throw new NotImplementedException(SR.NotImplementedByDesign); + 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; + } + + /// + /// ControlDesigner overrides this method to handle after-drop cases. + /// + public override void InitializeExistingComponent(IDictionary defaultValues) + { + base.InitializeExistingComponent(defaultValues); + // unhook any sited children that got ChildWindowTargets + foreach (Control c in Control.Controls) + { + if (c != null) + { + ISite site = c.Site; + if (site != null && c.WindowTarget is ChildWindowTarget target) + { + 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. + /// + public override void InitializeNewComponent(IDictionary defaultValues) + { + 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. + /// + [Obsolete("This method has been deprecated. Use InitializeNewComponent instead. http://go.microsoft.com/fwlink/?linkid=14202")] + public override void OnSetComponentDefaults() + { + 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 + /// + protected virtual void OnContextMenu(int x, int y) + { + ShowContextMenu(x, y); + } + + /// + /// This is called immediately after the control handle has been created. + /// + protected virtual void OnCreateHandle() + { + OnHandleChange(); + if (_revokeDragDrop) + { + UnsafeNativeMethods.RevokeDragDrop(Control.Handle); + } + } + + /// + /// Called when a drag-drop operation enters the control designer view + /// + 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(OnDragEnter); + control.DragEnter -= handler; + ((IDropTarget)Control).OnDragEnter(de); + control.DragEnter += handler; + } + + /// + /// Called to cleanup a D&D operation + /// + protected virtual void OnDragComplete(DragEventArgs de) + { + // default implementation - does nothing. + } + + /// + /// Called when a drag drop object is dropped onto the control designer view + /// + 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(OnDragDrop); + control.DragDrop -= handler; + ((IDropTarget)Control).OnDragDrop(de); + control.DragDrop += handler; + OnDragComplete(de); + } + + /// + /// Called when a drag-drop operation leaves the control designer view + /// + 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(OnDragLeave); + control.DragLeave -= handler; + ((IDropTarget)Control).OnDragLeave(e); + control.DragLeave += handler; + } + + /// + /// Called when a drag drop object is dragged over the control designer view + /// + 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(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. + /// + 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. + /// + protected virtual void OnMouseDragBegin(int x, int y) + { + // 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. + /// + protected virtual void OnMouseDragEnd(bool cancel) + { + _mouseDragLast = InvalidPoint; + Control.Capture = false; + if (!_mouseDragMoved) + { + // 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. + /// + protected virtual void OnMouseDragMove(int x, int y) + { + 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. + /// + protected virtual void OnMouseEnter() + { + 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() + { + 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. + /// + protected virtual void OnMouseLeave() + { + 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. + /// + protected virtual void OnPaintAdornments(PaintEventArgs pe) + { + + // 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. + /// + protected virtual void OnSetCursor() + { + 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; + } + } + + 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. + /// + protected override void PreFilterProperties(IDictionary properties) + { + 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. + /// + protected void UnhookChildControls(Control firstChild) + { + 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. + /// + protected virtual void WndProc(ref Message m) + { + 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(!s_inContextMenu, "Recursively hitting live region for context menu!!!"); + s_inContextMenu = true; + } + + try + { + DefWndProc(ref m); + } + finally + { + if (m.Msg == NativeMethods.WM_CONTEXTMENU) + { + s_inContextMenu = false; + } + if (m.Msg == NativeMethods.WM_LBUTTONUP) + { + // terminate the drag. 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)) + { + 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) + { + 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); + 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 + { + 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) + { + OnPaintAdornments(pevent); + } + else + { + NativeMethods.PAINTSTRUCT ps = new NativeMethods.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 (_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 (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)); + + 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; + } + } + + private void PaintException(PaintEventArgs e, Exception ex) + { + 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; + } + + 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 + { + get => _control.AccessibilityObject.Bounds; + } + + 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 DefaultAction + { + get => ""; + } + + public override string Name + { + get => _control.Name; + } + + public override AccessibleObject Parent + { + get => _control.AccessibilityObject.Parent; + } + + public override AccessibleRole Role + { + get => _control.AccessibilityObject.Role; + } + + private ISelectionService SelectionService + { + get + { + if (_selSvc == null) + { + _selSvc = (ISelectionService)_designer.GetService(typeof(ISelectionService)); + } + return _selSvc; + } + } + + 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 + { + get => _control.AccessibilityObject.Value; + } + + public override AccessibleObject GetChild(int index) + { + 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() => _control.AccessibilityObject.GetChildCount(); + + private AccessibleObject GetDesignerAccessibleObject(Control.ControlAccessibleObject cao) + { + if (cao == null) + { + return null; + } + if (DesignerHost.GetDesigner(cao.Owner) is ControlDesigner ctlDesigner) + { + return ctlDesigner.AccessibilityObject; + } + return null; + } + + public override AccessibleObject GetFocused() + { + if ((State & AccessibleStates.Focused) != 0) + { + return this; + } + return base.GetFocused(); + } + + public override AccessibleObject GetSelected() + { + if ((State & AccessibleStates.Selected) != 0) + { + return this; + } + return base.GetFocused(); + } + + public override AccessibleObject HitTest(int x, int y) => _control.AccessibilityObject.HitTest(x, y); + } + + /// + /// 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 + { + readonly ControlDesigner _designer; + Rectangle _controlRect = Rectangle.Empty; + + /// + /// Constructor that accepts the related ControlDesigner. + /// + internal TransparentBehavior(ControlDesigner designer) + { + _designer = designer; } /// - /// Forwards DragDrop notification from the BehaviorService to - /// the related ControlDesigner. + /// 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) { - 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); + } + } + + [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)) + { + // 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; + } + } + + // 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); + } + } + + private bool IsWindowInCurrentProcess(IntPtr hwnd) + { + SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(null, hwnd), out int pid); + return pid == CurrentProcessId; + } + + private int CurrentProcessId + { + get + { + if (s_currentProcessId == 0) + { + s_currentProcessId = SafeNativeMethods.GetCurrentProcessId(); + } + return s_currentProcessId; + } + } + + 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) + { + _designer = designer; + if (designer != null) + { + designer.DisposingHandler += new EventHandler(OnDesignerDisposing); + } + AssignHandle(hwnd); + } + + 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) + { + if (_designer == null) + { + 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; + } + } + } + } + + internal void RemoveSubclassedWindow(IntPtr hwnd) + { + if (SubclassedChildWindows.ContainsKey(hwnd)) + { + 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) + { + _designer.Control.WindowTarget = _oldTarget; + _designer = null; + } + } + + public void OnHandleChange(IntPtr newHandle) + { + _oldTarget.OnHandleChange(newHandle); + if (newHandle != IntPtr.Zero) + { + _designer.OnHandleChange(); + } + } + + public void OnMessage(ref Message m) + { + // 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) + { + 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); + } + } + } + + 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) + { + _designer = designer; + _childControl = childControl; + _oldWindowTarget = oldWindowTarget; + } + + public IWindowTarget OldWindowTarget + { + get => _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"); + UnsafeNativeMethods.RevokeDragDrop(_handle); + } + } + } + } + + internal void SetUnhandledException(Control owner, Exception exception) + { + if (_thrownException == null) + { + _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); + } + } + + [ListBindable(false)] + [DesignerSerializer(typeof(DesignerControlCollectionCodeDomSerializer), typeof(CodeDomSerializer))] + internal class DesignerControlCollection : Control.ControlCollection, IList + { + readonly Control.ControlCollection _realCollection; + public DesignerControlCollection(Control owner) : base(owner) + { + _realCollection = owner.Controls; + } + + public override int Count + { + get => _realCollection.Count; + } + + object ICollection.SyncRoot + { + get => this; + } + + bool ICollection.IsSynchronized + { + get => false; + } + + bool IList.IsFixedSize + { + get => false; + } + + public new bool IsReadOnly + { + 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 => ((IList)_realCollection)[index]; + set => throw new NotSupportedException(); + } + + 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) => _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); + } + } + } + } + + // 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); + } + } + + 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(); + } + } + } + } + + private class CanResetSizePropertyDescriptor : PropertyDescriptor + { + private readonly PropertyDescriptor _basePropDesc; + + public CanResetSizePropertyDescriptor(PropertyDescriptor pd) : base(pd) + { + _basePropDesc = pd; + } + + public override Type ComponentType + { + get => _basePropDesc.ComponentType; + } + + public override string DisplayName + { + get => _basePropDesc.DisplayName; + } + + public override bool IsReadOnly + { + get => _basePropDesc.IsReadOnly; + } + + public override Type PropertyType + { + 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/DesignerActionPanel.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionPanel.cs deleted file mode 100644 index 2fc3992b6ba..00000000000 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionPanel.cs +++ /dev/null @@ -1,3171 +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.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.Runtime.Versioning; -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; - - 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; - 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; - } - } - - 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) - { - 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) - { - return _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(nameof(actionPanel)); - } - - protected DesignerActionPanel ActionPanel - { - get => _actionPanel; - } - - public abstract string FocusId { get; } - - public IServiceProvider ServiceProvider => _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) - { - return 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 => _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; - } - public DesignerActionList ActionList { get => _actionList; set => _actionList = value; } - - 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); - - 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) - { - _formActive = true; - ActionPanel.Invalidate(); - } - - private void OnFormDeactivate(object sender, EventArgs e) - { - _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; } - - 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; - 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; - } - } - - 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; - try - { - OnValueChanged(); - } - finally - { - _pushingValue = false; - } - } - } - - 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; } - - protected Point EditRegionLocation { get; } - - protected Point EditRegionRelativeLocation { get; } - - protected Size EditRegionSize { get; } - - public Point EditRegionRelativeLocation1 { get => _editRegionRelativeLocation; set => _editRegionRelativeLocation = value; } - - 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) - { - return TextBoxLineInnerPadding; - } - - protected virtual int GetTextBoxRightPadding(int textBoxHeight) - { - return 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); - EditRegionRelativeLocation1 = 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() - { - return 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() - { - return 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.s_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) - { - return 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); - } - } - - // Mostly copied from WinForms\Managed\System\WinForms\PropertyGridInternal\PropertyGridView.cs - 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) - { - Application.DoEvents(); - 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)); - IntPtr hWndCapture = UnsafeNativeMethods.GetCapture(); - if (hWndCapture != IntPtr.Zero) - { - UnsafeNativeMethods.SendMessage(new HandleRef(null, hWndCapture), NativeMethods.WM_CANCELMODE, 0, 0); - SafeNativeMethods.ReleaseCapture(); - } - - Visible = true; - FocusComponent(); - DoModalLoop(); - } - finally - { - UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(null, IntPtr.Zero)); - - // sometimes activation gets messed up - 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) - { - return n & 0xffff; - } - } - - public static class CommonHandles - { - public static HandleCollector s_gdiHandleCollector = new HandleCollector("GDI", 500); - public static HandleCollector s_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)] - [ResourceExposure(ResourceScope.None)] - private static extern bool IntDeleteObject(HandleRef hObject); - public static bool DeleteObject(HandleRef hObject) - { - NativeMethods.CommonHandles.s_gdiHandleCollector.Remove(); - return IntDeleteObject(hObject); - } - - [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - public static extern bool ReleaseCapture(); - - [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - public static extern IntPtr SelectObject(HandleRef hDC, HandleRef hObject); - - [DllImport(ExternDllGdi32, SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - 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)] - [ResourceExposure(ResourceScope.None)] - 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)] - [ResourceExposure(ResourceScope.None)] - 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")] - [ResourceExposure(ResourceScope.None)] - public static extern IntPtr GetWindowLong(HandleRef hWnd, int nIndex); - - [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] - [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] - [ResourceExposure(ResourceScope.None)] - public static extern IntPtr SetWindowLong(HandleRef hWnd, int nIndex, HandleRef dwNewLong); - - [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - 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)] - [ResourceExposure(ResourceScope.None)] - public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam); - - [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - public static extern IntPtr GetCapture(); - - [DllImport(ExternDllUser32, ExactSpelling = true, EntryPoint = "GetDC", CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.Process)] - private static extern IntPtr IntGetDC(HandleRef hWnd); - public static IntPtr GetDC(HandleRef hWnd) - { - NativeMethods.CommonHandles.s_hdcHandleCollector.Add(); - return IntGetDC(hWnd); - } - - [DllImport(ExternDllUser32, ExactSpelling = true, EntryPoint = "ReleaseDC", CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - private static extern int IntReleaseDC(HandleRef hWnd, HandleRef hDC); - public static int ReleaseDC(HandleRef hWnd, HandleRef hDC) - { - NativeMethods.CommonHandles.s_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 - { - return 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() - { - return 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(System.Collections.Generic.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) - { - return 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/Windows/Forms/Design/DesignerActionService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerActionService.cs index cb4476759c2..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 @@ -9,18 +9,16 @@ 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. + /// 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 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 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. + // 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; /// @@ -28,7 +26,6 @@ public class DesignerActionService : IDisposable /// public DesignerActionService(IServiceProvider serviceProvider) { - if (serviceProvider != null) { _serviceProvider = serviceProvider; @@ -47,9 +44,6 @@ public DesignerActionService(IServiceProvider serviceProvider) } _designerActionLists = new Hashtable(); _componentToVerbsEventHookedUp = new Hashtable(); -#if DEBUGDESIGNERTASKS - Debug.WriteLine("DesignerActionService - created"); -#endif } /// @@ -57,14 +51,8 @@ public DesignerActionService(IServiceProvider serviceProvider) /// public event DesignerActionListsChangedEventHandler DesignerActionListsChanged { - add - { - _designerActionListsChanged += value; - } - remove - { - _designerActionListsChanged -= value; - } + add => _designerActionListsChanged += value; + remove => _designerActionListsChanged -= value; } /// @@ -90,6 +78,8 @@ public void Add(IComponent comp, DesignerActionListCollection designerActionList { _designerActionLists.Add(comp, designerActionListCollection); } + + //fire event OnDesignerActionListsChanged(new DesignerActionListsChangedEventArgs(comp, DesignerActionListsChangedType.ActionListsAdded, GetComponentActions(comp))); } @@ -98,7 +88,7 @@ public void Add(IComponent comp, DesignerActionListCollection designerActionList /// public void Add(IComponent comp, DesignerActionList actionList) { - Add(comp, new DesignerActionListCollection( new [] { actionList } )); + Add(comp, new DesignerActionListCollection( new[] { actionList } )); } /// @@ -106,7 +96,6 @@ public void Add(IComponent comp, DesignerActionList actionList) /// public void Clear() { - if (_designerActionLists.Count == 0) { return; @@ -121,7 +110,6 @@ public void Clear() //actually clear our hashtable _designerActionLists.Clear(); - //fire our DesignerActionsChanged event for each comp we just removed foreach (Component comp in compsRemoved) { @@ -173,7 +161,6 @@ public DesignerActionListCollection GetComponentActions(IComponent component) return GetComponentActions(component, ComponentActionsType.All); } - public virtual DesignerActionListCollection GetComponentActions(IComponent component, ComponentActionsType type) { if (component == null) @@ -193,7 +180,6 @@ public virtual DesignerActionListCollection GetComponentActions(IComponent compo case ComponentActionsType.Service: GetComponentServiceActions(component, result); break; - } return result; } @@ -237,12 +223,10 @@ protected virtual void GetComponentDesignerActions(IComponent component, Designe { if (hookupEvents) { - //Debug.WriteLine("hooking up change event for verb " + verb.Text); verb.CommandChanged += new EventHandler(OnVerbStatusChanged); } if (verb.Enabled && verb.Visible) { - //Debug.WriteLine("adding verb to collection for panel... " + verb.Text); verbsArray.Add(verb); } } @@ -254,7 +238,7 @@ protected virtual void GetComponentDesignerActions(IComponent component, Designe } } - // 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... + // 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) { @@ -290,7 +274,6 @@ private void OnVerbStatusChanged(object sender, EventArgs args) DesignerActionUIService dapUISvc = (DesignerActionUIService)sc.GetService(typeof(DesignerActionUIService)); if (dapUISvc != null) { - //Debug.WriteLine("Calling refresh on component " + comp.Site.Name); dapUISvc.Refresh(comp); // we need to refresh, a verb on the current panel has changed its state } } @@ -321,7 +304,7 @@ protected virtual void GetComponentServiceActions(IComponent component, Designer 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... + // 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(); @@ -334,7 +317,7 @@ protected virtual void GetComponentServiceActions(IComponent component, Designer } /// - /// We hook the OnComponentRemoved event so we can clean up all associated actions. + /// We hook the OnComponentRemoved event so we can clean up all associated actions. /// private void OnComponentRemoved(object source, ComponentEventArgs ce) { @@ -365,12 +348,11 @@ public void Remove(IComponent comp) } _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. + /// 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) { @@ -391,7 +373,7 @@ 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) { @@ -409,7 +391,6 @@ public void Remove(IComponent comp, DesignerActionList actionList) } DesignerActionListCollection actionLists = (DesignerActionListCollection)_designerActionLists[comp]; - if (!actionLists.Contains(actionList)) { return; @@ -437,7 +418,6 @@ public void Remove(IComponent comp, DesignerActionList actionList) { actionLists.Remove(t); } - OnDesignerActionListsChanged(new DesignerActionListsChangedEventArgs(comp, DesignerActionListsChangedType.ActionListsRemoved, GetComponentActions(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 b5baccfd107..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 @@ -3,23 +3,18 @@ // 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.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. + /// 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 { @@ -32,14 +27,14 @@ internal class DesignerActionUI : IDisposable 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 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; + internal DesignerActionToolStripDropDown designerActionHost; private readonly MenuCommand _cmdShowDesignerActions; //used to respond to the Alt+Shft+F10 command private bool _inTransaction = false; @@ -49,27 +44,21 @@ internal class DesignerActionUI : IDisposable private readonly bool _disposeActionUIService; private delegate void ActionChangedEventHandler(object sender, DesignerActionListsChangedEventArgs e); - #if DEBUG - internal static readonly TraceSwitch s_dropDownVisibilityDebug = new TraceSwitch("DropDownVisibilityDebug", "Debug ToolStrip Selection code"); + internal static readonly TraceSwitch DropDownVisibilityDebug = new TraceSwitch("DropDownVisibilityDebug", "Debug ToolStrip Selection code"); #else - internal static readonly TraceSwitch s_dropDownVisibilityDebug; + 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. + /// 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."); @@ -100,7 +89,6 @@ public DesignerActionUI(IServiceProvider serviceProvider, Adorner containerAdorn cs.ComponentChanged += new ComponentChangedEventHandler(OnComponentChanged); } - if (_menuCommandService != null) { _cmdShowDesignerActions = new MenuCommand(new EventHandler(OnKeyShowDesignerActions), MenuCommands.KeyInvokeSmartTag); @@ -110,9 +98,7 @@ public DesignerActionUI(IServiceProvider serviceProvider, Adorner containerAdorn _uiService = (IUIService)serviceProvider.GetService(typeof(IUIService)); if (_uiService != null) _mainParentWindow = _uiService.GetDialogOwnerWindow(); - _componentToGlyph = new Hashtable(); - _marshalingControl = new Control(); _marshalingControl.CreateControl(); } @@ -120,7 +106,8 @@ public DesignerActionUI(IServiceProvider serviceProvider, Adorner containerAdorn /// /// Disposes all UI-related objects and unhooks services. /// - [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] + // Don't need to dispose of designerActionUIService. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] public void Dispose() { if (_marshalingControl != null) @@ -128,7 +115,6 @@ public void Dispose() _marshalingControl.Dispose(); _marshalingControl = null; } - if (_serviceProvider != null) { IComponentChangeService cs = (IComponentChangeService)_serviceProvider.GetService(typeof(IComponentChangeService)); @@ -150,7 +136,6 @@ public void Dispose() _serviceProvider = null; _behaviorService = null; _selSvc = null; - if (_designerActionService != null) { _designerActionService.DesignerActionListsChanged -= new DesignerActionListsChangedEventHandler(OnDesignerActionsChanged); @@ -200,7 +185,7 @@ internal DesignerActionGlyph GetDesignerActionGlyph(IComponent comp, DesignerAct { 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 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 @@ -215,7 +200,7 @@ internal DesignerActionGlyph GetDesignerActionGlyph(IComponent comp, DesignerAct } } - // 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. + //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 @@ -248,26 +233,26 @@ internal DesignerActionGlyph GetDesignerActionGlyph(IComponent comp, DesignerAct RemoveActionGlyph(comp); return null; } - } /// - /// We monitor this event so we can update smart tag locations when controls move. + /// 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 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 something changed on a component we have actions associated with then invalidate all (repaint & reposition) if (_componentToGlyph[ce.Component] is DesignerActionGlyph glyph) { glyph.Invalidate(); @@ -290,7 +275,7 @@ private void OnComponentChanged(object source, ComponentChangedEventArgs ce) private void RecreatePanel(IComponent comp) { if (_inTransaction || comp != _selSvc.PrimarySelection) - { //we only ever need to do that when the comp is the primary selection + { // 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. @@ -303,7 +288,6 @@ private void RecreatePanel(IComponent comp) } if (host.InTransaction && !hostIsClosingTransaction) { - //Debug.WriteLine("In transaction, bail, but first hookup to the end of the transaction..."); host.TransactionClosed += new DesignerTransactionCloseEventHandler(DesignerTransactionClosed); _inTransaction = true; _relatedComponentTransaction = comp; @@ -317,7 +301,7 @@ private void DesignerTransactionClosed(object sender, DesignerTransactionCloseEv { 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 + // 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); @@ -328,11 +312,11 @@ 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) { - 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... + 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 } @@ -342,7 +326,7 @@ private void RecreatePanel(Glyph glyphWithPanelToRegen) // we don't want to do anything if the panel is not visible if (!IsDesignerActionPanelVisible) { - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.RecreatePanel] panel is not visible, bail"); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.RecreatePanel] panel is not visible, bail"); return; } //recreate a designeraction panel @@ -351,10 +335,10 @@ private void RecreatePanel(Glyph glyphWithPanelToRegen) 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 + 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(); + designerActionHost.UpdateContainerSize(); } } } @@ -380,8 +364,7 @@ private void VerifyGlyphIsInAdorner(DesignerActionGlyph glyph) } /// - /// 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. + /// 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) { @@ -436,8 +419,8 @@ private void OnDesignerActionUIStateChange(object sender, DesignerActionUIStateC /// private void OnInvokedDesignerActionChanged(object sender, DesignerActionListsChangedEventArgs e) { - DesignerActionGlyph g = null; IComponent relatedComponent = e.RelatedObject as IComponent; + DesignerActionGlyph g = null; if (e.ChangeType == DesignerActionListsChangedType.ActionListsAdded) { if (relatedComponent == null) @@ -445,7 +428,6 @@ private void OnInvokedDesignerActionChanged(object sender, DesignerActionListsCh 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) { @@ -460,10 +442,9 @@ private void OnInvokedDesignerActionChanged(object sender, DesignerActionListsCh } } } - if (e.ChangeType == DesignerActionListsChangedType.ActionListsRemoved && e.ActionLists.Count == 0) { - // only remove our glyph if there are no more DesignerActions associated with it. + //only remove our glyph if there are no more DesignerActions associated with it. RemoveActionGlyph(e.RelatedObject); } else if (g != null) @@ -474,23 +455,23 @@ private void OnInvokedDesignerActionChanged(object sender, DesignerActionListsCh } /// - /// 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. + /// 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 + // 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)) { @@ -500,6 +481,7 @@ internal bool ShowDesignerActionPanelForPrimarySelection() DesignerActionGlyph glyph = (DesignerActionGlyph)_componentToGlyph[primarySelection]; if (glyph != null && glyph.Behavior is DesignerActionBehavior) { + // show the menu if (glyph.Behavior is DesignerActionBehavior behavior) { if (!IsDesignerActionPanelVisible) @@ -526,7 +508,6 @@ internal void RemoveActionGlyph(object relatedObject) { return; } - if (IsDesignerActionPanelVisible && relatedObject == _lastPanelComponent) { HideDesignerActionPanel(); @@ -535,7 +516,6 @@ internal void RemoveActionGlyph(object relatedObject) DesignerActionGlyph glyph = (DesignerActionGlyph)_componentToGlyph[relatedObject]; if (glyph != null) { - // Check ComponentTray first if (_serviceProvider.GetService(typeof(ComponentTray)) is ComponentTray compTray && compTray.SelectionGlyphs != null) { @@ -551,7 +531,7 @@ internal void RemoveActionGlyph(object relatedObject) } _componentToGlyph.Remove(relatedObject); - // we only do this when we're in a transaction. This is for compat reason - infragistic. if we're not in a transaction, too bad, we don't update the screen + // 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); @@ -569,7 +549,6 @@ private void InvalidateGlyphOnLastTransaction(object sender, DesignerTransaction { host.TransactionClosed -= new DesignerTransactionCloseEventHandler(InvalidateGlyphOnLastTransaction); } - if (_relatedGlyphTransaction != null) { _relatedGlyphTransaction.InvalidateOwnerLocation(); @@ -582,24 +561,18 @@ internal void HideDesignerActionPanel() { if (IsDesignerActionPanelVisible) { - _designerActionHost.Close(); + designerActionHost.Close(); } } internal bool IsDesignerActionPanelVisible { - get - { - return (_designerActionHost != null && _designerActionHost.Visible); - } + get => (designerActionHost != null && designerActionHost.Visible); } internal IComponent LastPanelComponent { - get - { - return (IsDesignerActionPanelVisible ? _lastPanelComponent : null); - } + get => (IsDesignerActionPanelVisible ? _lastPanelComponent : null); } private void ToolStripDropDown_Closing(object sender, ToolStripDropDownClosingEventArgs e) @@ -607,29 +580,28 @@ private void ToolStripDropDown_Closing(object sender, ToolStripDropDownClosingEv if (_cancelClose || e.Cancel) { e.Cancel = true; - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] cancelClose true, bail"); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] cancelClose true, bail"); return; } if (e.CloseReason == ToolStripDropDownCloseReason.ItemClicked) { e.Cancel = true; - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] ItemClicked: e.Cancel set to: " + e.Cancel.ToString()); + 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.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] Keyboard: e.Cancel set to: " + e.Cancel.ToString()); + 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.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] Closing..."); + 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... + // 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) { @@ -641,10 +613,7 @@ private void ToolStripDropDown_Closing(object sender, ToolStripDropDownClosingEv } currentGlyph.InvalidateOwnerLocation(); } - - // unset the ownership relationship _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); @@ -659,14 +628,14 @@ internal Point UpdateDAPLocation(IComponent component, DesignerActionGlyph glyph component = _lastPanelComponent; } - if (_designerActionHost == null) + if (designerActionHost == null) { return Point.Empty; } if (component == null || glyph == null) { - return _designerActionHost.Location; + return designerActionHost.Location; } // check that the glyph is still visible in the adorner window @@ -674,14 +643,14 @@ internal Point UpdateDAPLocation(IComponent component, DesignerActionGlyph glyph !_behaviorService.AdornerWindowControl.DisplayRectangle.IntersectsWith(glyph.Bounds)) { HideDesignerActionPanel(); - return _designerActionHost.Location; + 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); + Point pt = DesignerActionPanel.ComputePreferredDesktopLocation(rectGlyph, designerActionHost.Size, out DockStyle edgeToDock); glyph.DockEdge = edgeToDock; - _designerActionHost.Location = pt; + designerActionHost.Location = pt; return pt; } @@ -692,7 +661,7 @@ private Point GetGlyphLocationScreenCoord(IComponent relatedComponent, Glyph gly { 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 + //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) @@ -711,30 +680,27 @@ private Point GetGlyphLocationScreenCoord(IComponent relatedComponent, Glyph gly } 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) + if (designerActionHost == null) { - _designerActionHost = new DesignerActionToolStripDropDown(this, _mainParentWindow) + designerActionHost = new DesignerActionToolStripDropDown(this, _mainParentWindow) { AutoSize = false, Padding = Padding.Empty, Renderer = new NoBorderRenderer(), Text = "DesignerActionTopLevelForm" }; - _designerActionHost.Closing += new ToolStripDropDownClosingEventHandler(ToolStripDropDown_Closing); - - + 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); + designerActionHost.AccessibleName = string.Format(SR.DesignerActionPanel_DefaultPanelTitle, relatedComponent.GetType().Name); panel.AccessibleName = string.Format(SR.DesignerActionPanel_DefaultPanelTitle, relatedComponent.GetType().Name); - _designerActionHost.SetDesignerActionPanel(panel, glyph); + 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 @@ -744,25 +710,20 @@ internal void ShowDesignerActionPanel(IComponent relatedComponent, DesignerActio if (_mainParentWindow != null && _mainParentWindow.Handle != IntPtr.Zero) { Debug.WriteLineIf(s_designeActionPanelTraceSwitch.TraceVerbose, "Assigning owner to mainParentWindow"); - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "Assigning owner to mainParentWindow"); - UnsafeNativeMethods.SetWindowLong(new HandleRef(_designerActionHost, _designerActionHost.Handle), - NativeMethods.GWL_HWNDPARENT, - new HandleRef(_mainParentWindow, _mainParentWindow.Handle)); + 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(); + 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)); - + 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); + _dapkb = new DesignerActionKeyboardBehavior(designerActionHost.CurrentPanel, _serviceProvider, _behaviorService); _behaviorService.PushBehavior(_dapkb); } } @@ -770,12 +731,11 @@ internal void ShowDesignerActionPanel(IComponent relatedComponent, DesignerActio 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) + if (designerActionHost != null && designerActionHost.Handle != IntPtr.Zero && designerActionHost.Visible) { - UnsafeNativeMethods.SetActiveWindow(new HandleRef(this, _designerActionHost.Handle)); - _designerActionHost.CheckFocusIsRight(); + UnsafeNativeMethods.SetActiveWindow(new HandleRef(this, designerActionHost.Handle)); + designerActionHost.CheckFocusIsRight(); } } } @@ -786,7 +746,6 @@ internal class DesignerActionToolStripDropDown : ToolStripDropDown private ToolStripControlHost _panel; private readonly DesignerActionUI _designerActionUI; private bool _cancelClose = false; - private Glyph _relatedGlyph; public DesignerActionToolStripDropDown(DesignerActionUI designerActionUI, IWin32Window mainParentWindow) @@ -795,7 +754,6 @@ public DesignerActionToolStripDropDown(DesignerActionUI designerActionUI, IWin32 _designerActionUI = designerActionUI; } - public DesignerActionPanel CurrentPanel { get @@ -814,7 +772,7 @@ public DesignerActionPanel CurrentPanel // we're not topmost because we can show modal editors above us. protected override bool TopMost { - get { return false; } + get => false; } public void UpdateContainerSize() @@ -836,21 +794,21 @@ public void UpdateContainerSize() } public void CheckFocusIsRight() - { // hack to get the focus to NOT stay on ContainerControl - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "Checking focus..."); + { // fix 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.s_dropDownVisibilityDebug.TraceVerbose, " putting focus on the panel..."); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, " putting focus on the panel..."); _panel.Focus(); } focusedControl = UnsafeNativeMethods.GetFocus(); if (CurrentPanel != null && CurrentPanel.Handle == focusedControl) { - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, " selecting next available control on the panel..."); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, " selecting next available control on the panel..."); CurrentPanel.SelectNextControl(null, true, true, true, true); } - _ = UnsafeNativeMethods.GetFocus(); + UnsafeNativeMethods.GetFocus(); } protected override void OnLayout(LayoutEventArgs levent) @@ -862,31 +820,31 @@ protected override void OnLayout(LayoutEventArgs levent) protected override void OnClosing(ToolStripDropDownClosingEventArgs e) { - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "_____________________________Begin OnClose " + e.CloseReason.ToString()); + + 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.s_dropDownVisibilityDebug.TraceVerbose, "cancel close prepopulated"); + 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. + // 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.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] activation hasnt changed, but we've certainly clicked somewhere else."); + 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.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] Cancel close - the window activating is owned by this window"); + 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)) { @@ -894,55 +852,45 @@ protected override void OnClosing(ToolStripDropDownClosingEventArgs e) { // 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.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] Call close: the activated windows is not a child/owned windows of the main top level windows "); + 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.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] we're being deactivated by a foreign window, but the main window is not enabled - we should stay up"); + 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.s_dropDownVisibilityDebug.TraceVerbose, "_____________________________End OnClose e.Cancel: " + e.Cancel.ToString()); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "_____________________________End OnClose e.Cancel: " + e.Cancel.ToString()); return; } else { - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] since the designer action panel dropdown doesnt own the activating window " + hwndActivating.ToString("x") + ", calling close. "); + 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.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] modal window activated - cancelling close"); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] modal window activated - cancelling close"); // we are in a modal case e.Cancel = true; } } - else - { - } - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] calling base.OnClosing with e.Cancel: " + e.Cancel.ToString()); - + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] calling base.OnClosing with e.Cancel: " + e.Cancel.ToString()); base.OnClosing(e); Debug.Unindent(); - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "_____________________________End OnClose e.Cancel: " + e.Cancel.ToString()); - + 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) @@ -962,7 +910,6 @@ public void SetDesignerActionPanel(DesignerActionPanel panel, Glyph relatedGlyph Size = panel.Size; Items.Add(_panel); ResumeLayout(); - if (Visible) { CheckFocusIsRight(); @@ -970,7 +917,7 @@ public void SetDesignerActionPanel(DesignerActionPanel panel, Glyph relatedGlyph } - private void PanelResized(object sender, EventArgs e) + private void PanelResized(object sender, System.EventArgs e) { Control ctrl = sender as Control; if (Size.Width != ctrl.Size.Width || Size.Height != ctrl.Size.Height) @@ -988,7 +935,7 @@ private void PanelResized(object sender, EventArgs e) protected override void SetVisibleCore(bool visible) { - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.SetVisibleCore] setting dropdown visible=" + visible.ToString()); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.SetVisibleCore] setting dropdown visible=" + visible.ToString()); base.SetVisibleCore(visible); if (visible) { @@ -1002,21 +949,18 @@ protected override void SetVisibleCore(bool visible) /// private static bool WindowOwnsWindow(IntPtr hWndOwner, IntPtr hWndDescendant) { - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "[WindowOwnsWindow] Testing if " + hWndOwner.ToString("x") + " is a owned by " + hWndDescendant.ToString("x") + "... "); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[WindowOwnsWindow] Testing if " + hWndOwner.ToString("x") + " is a owned by " + hWndDescendant.ToString("x") + "... "); #if DEBUG - if (DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose) - { + 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)); + Debug.WriteLine("OWNEE's CLAIMED OWNER: "+ GetControlInformation(claimedOwnerHwnd)); } - #endif if (hWndDescendant == hWndOwner) { - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "they match, YES."); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "they match, YES."); return true; } @@ -1025,19 +969,18 @@ private static bool WindowOwnsWindow(IntPtr hWndOwner, IntPtr hWndDescendant) hWndDescendant = UnsafeNativeMethods.GetWindowLong(new HandleRef(null, hWndDescendant), NativeMethods.GWL_HWNDPARENT); if (hWndDescendant == IntPtr.Zero) { - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "NOPE."); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "NOPE."); return false; } if (hWndDescendant == hWndOwner) { - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "YES."); + Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "YES."); return true; } } - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "NO."); + 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) { @@ -1046,27 +989,21 @@ internal static string GetControlInformation(IntPtr hwnd) return "Handle is IntPtr.Zero"; } #if DEBUG - if (!DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose) - { + 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) - { + } + 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)) - { + if (!string.IsNullOrEmpty(c.Name)) { nameOfControl += c.Name; } - else - { + else { nameOfControl += "Unknown"; // some extra debug info for toolstripdropdowns... if (c is ToolStripDropDown dd) @@ -1077,10 +1014,10 @@ internal static string GetControlInformation(IntPtr hwnd) } } } - } - return sb.ToString() + "\r\n\t\t\tType: [" + typeOfControl + "] Name: [" + nameOfControl + "]"; -#else - return String.Empty; + } + return sb.ToString() + "\r\n\t\t\tType: [" + typeOfControl + "] Name: [" + nameOfControl + "]"; +#else + return string.Empty; #endif } @@ -1097,10 +1034,8 @@ private void WmActivate(ref Message m) IntPtr hwndActivating = m.LParam; if (WindowOwnsWindow(Handle, hwndActivating)) { - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI WmActivate] setting cancel close true because WindowsOwnWindow"); - - Debug.WriteLineIf(DesignerActionUI.s_dropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI WmActivate] checking the focus... " + GetControlInformation(UnsafeNativeMethods.GetFocus())); - + 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 @@ -1112,7 +1047,6 @@ private void WmActivate(ref Message m) { _cancelClose = false; } - base.WndProc(ref m); } @@ -1129,7 +1063,7 @@ protected override void WndProc(ref Message 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 + // 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(); 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 db24cc17e38..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 @@ -35,10 +35,6 @@ public override bool IncludeAsDesignerVerb get => false; } - public override void Invoke() - { - _targetVerb.Invoke(); - } - + public override void Invoke() => _targetVerb.Invoke(); } } 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 3bb73cc2163..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 @@ -15,7 +15,7 @@ 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: + /// 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 @@ -39,11 +39,9 @@ internal class DesignerFrame : Control, IOverlayService, ISplitWindowService, IC [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) { @@ -53,7 +51,6 @@ public DesignerFrame(ISite site) } } 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; @@ -92,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) { @@ -121,16 +117,10 @@ public void Initialize(Control view) { 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); } /// @@ -139,7 +129,6 @@ public void Initialize(Control view) protected override void OnGotFocus(EventArgs e) { ForceDesignerRedraw(true); - ISelectionService selSvc = (ISelectionService)_designerSite.GetService(typeof(ISelectionService)); if (selSvc != null) { @@ -193,10 +182,8 @@ protected override bool ProcessDialogKey(Keys keyData) void SyncDesignerUI() { Size selectionSize = DesignerUtils.GetAdornmentDimensions(AdornmentType.Maximum); - _designerRegion.AutoScrollMargin = selectionSize; _designer.Location = new Point(selectionSize.Width, selectionSize.Height); - if (BehaviorService != null) { BehaviorService.SyncSelection(); @@ -220,12 +207,10 @@ protected override void WndProc(ref Message m) 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) { @@ -280,11 +265,8 @@ protected override void WndProc(ref Message 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) - { - return _designerRegion.PushOverlay(control); - } - + 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. /// @@ -338,17 +320,14 @@ void ISplitWindowService.AddSplitWindow(Control window) _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(); @@ -366,20 +345,14 @@ void ISplitWindowService.RemoveSplitWindow(Control window) } /// - /// 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. + /// 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 - }; + ThemedScrollbarWindow windowInfo = new ThemedScrollbarWindow { Handle = c.Handle }; if (c is OverlayControl) { windowInfo.Mode = ThemedScrollbarMode.OnlyTopLevel; @@ -388,10 +361,8 @@ IEnumerable IContainsThemedScrollbarWindows.ThemedScrollbarWindows() { windowInfo.Mode = ThemedScrollbarMode.All; } - windows.Add(windowInfo); } - return windows; } @@ -401,7 +372,7 @@ IEnumerable IContainsThemedScrollbarWindows.ThemedScrollbarWindows() private class OverlayControl : ScrollableControl { private readonly ArrayList _overlayList; - private IServiceProvider _provider; + private readonly IServiceProvider _provider; internal bool _messageMouseWheelProcessed; private BehaviorService _behaviorService; @@ -411,8 +382,7 @@ private class OverlayControl : ScrollableControl [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] public OverlayControl(IServiceProvider provider) { - OverlayControl overlayControl = this; - overlayControl._provider = provider; + _provider = provider; _overlayList = new ArrayList(); AutoScroll = true; Text = "OverlayControl"; @@ -560,10 +530,8 @@ public void InvalidateOverlays(Region screenRegion) 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 + // 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); @@ -571,7 +539,6 @@ public void InvalidateOverlays(Region screenRegion) } } } - /// /// Need to know when child windows are created so we can properly set the Z-order /// @@ -596,8 +563,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); } } } @@ -624,7 +590,10 @@ 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. + // 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; 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..8e5edf60a6d --- /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 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 + { + 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 index f294e624639..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 @@ -22,56 +22,47 @@ namespace System.Windows.Forms.Design internal static class DesignerUtils { private static Size s_minDragSize = Size.Empty; - - // brush used to draw a 'hover' state over a designer action glyph + //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 + //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 + //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) + // 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 + //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 + //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) + //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 + //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 + //lock handle height public static int LOCKHANDLEHEIGHT = 9; - // total lock handle width + //total lock handle width public static int LOCKHANDLEWIDTH = 7; - // how much should the lockhandle overlap the control + //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 + //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; @@ -81,39 +72,36 @@ internal static class DesignerUtils public static int LOCKHANDLEHEIGHT_LOWER = 6; public static int LOCKHANDLEWIDTH_LOWER = 7; - // Offset used when drawing the upper rect of a lock handle + //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 + //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 + //delay for showing snaplines on keyboard movements public static int SNAPELINEDELAY = 1000; - // min new row/col style size for the table layout panel + //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. + //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 + //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 + //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 + //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) + //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. + //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; /// @@ -132,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); @@ -174,10 +162,7 @@ public static Image BoxImage /// public static Brush HoverBrush { - get - { - return s_hoverBrush; - } + get => s_hoverBrush; } /// @@ -234,20 +219,19 @@ public static void SyncBrushes() /// 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 + //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 + //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)); @@ -279,7 +263,6 @@ public static void DrawFrame(Graphics g, Region resizeBorder, FrameStyle style, { color = SystemColors.ControlLight; } - switch (style) { case FrameStyle.Dashed: @@ -290,7 +273,6 @@ public static void DrawFrame(Graphics g, Region resizeBorder, FrameStyle style, brush = new SolidBrush(color); break; } - g.FillRegion(brush, resizeBorder); brush.Dispose(); } @@ -309,11 +291,11 @@ public static void DrawGrabHandle(Graphics graphics, Rectangle bounds, bool isPr //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); @@ -334,7 +316,6 @@ public static void DrawNoResizeHandle(Graphics graphics, Rectangle bounds, bool //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)); @@ -353,14 +334,11 @@ public static void DrawLockedHandle(Graphics graphics, Rectangle bounds, bool is 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); @@ -375,7 +353,6 @@ public static void DrawLockedHandle(Graphics graphics, Rectangle bounds, bool is } } - /// /// Uses the lockedBorderBrush to draw a 'locked' border on the given Graphics at the specified bounds. /// @@ -385,22 +362,18 @@ public static void DrawSelectionBorder(Graphics graphics, Rectangle 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. + /// 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... + //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 + //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 + //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 + //set the opacity if (opacity < 1.0 && opacity > 0.0) { // make this semi-transparent @@ -415,12 +388,10 @@ public static void GenerateSnapShot(Control control, ref Image image, int border 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. + /// 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) { @@ -484,7 +455,7 @@ public static object GetOptionValue(IServiceProvider provider, string name) /// public static void GenerateSnapShotWithBitBlt(Control control, ref Image image) { - // get the DC's and create our 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); @@ -495,10 +466,8 @@ public static void GenerateSnapShotWithBitBlt(Control control, ref Image image) { gDest.Clear(SystemColors.Control); } - IntPtr destDC = gDest.GetHdc(); - - // perform our bitblit operation to push the image into the dest bitmap + //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); @@ -512,7 +481,8 @@ 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. + + //Have to do this BEFORE we set the testcolor. if (control.BackColor == Color.Transparent) { using (Graphics g = Graphics.FromImage(image)) @@ -524,7 +494,6 @@ public static bool GenerateSnapShotWithWM_PRINT(Control control, ref Image image // 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(); @@ -539,7 +508,6 @@ public static bool GenerateSnapShotWithWM_PRINT(Control control, ref Image image //wm_print failed return false; } - return true; } @@ -638,7 +606,7 @@ public static int GetTextBaseline(Control ctrl, ContentAlignment alignment) { hFontOld = SafeNativeMethods.SelectObject(new HandleRef(ctrl, dc), new HandleRef(ctrl, hFont)); NativeMethods.TEXTMETRIC metrics = new NativeMethods.TEXTMETRIC(); - SafeNativeMethods.GetTextMetrics(new HandleRef(ctrl, dc), metrics); + UnsafeNativeMethods.GetTextMetrics(new HandleRef(ctrl, dc), metrics); //add the font ascent to the baseline fontAscent = metrics.tmAscent + 1; fontHeight = metrics.tmHeight; @@ -667,13 +635,21 @@ public static int GetTextBaseline(Control ctrl, ContentAlignment alignment) } /// - /// 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 + /// 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 + + //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 @@ -713,6 +689,7 @@ public static Rectangle GetBoundsFromToolboxSnapDragDropInfo(ToolboxSnapDragDrop } } } + return newBounds; } @@ -736,7 +713,6 @@ public static string GetUniqueSiteName(IDesignerHost host, string name) // 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! @@ -765,7 +741,6 @@ private static unsafe void SetImageAlpha(Bitmap b, double opacity) } 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++) { @@ -773,6 +748,7 @@ private static unsafe void SetImageAlpha(Bitmap b, double opacity) } // lock the data in ARGB format. + // BitmapData data = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); try { @@ -780,7 +756,8 @@ private static unsafe void SetImageAlpha(Bitmap b, double opacity) 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 + // 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 @@ -795,7 +772,6 @@ private static unsafe void SetImageAlpha(Bitmap b, double opacity) // now, apply the data back to the bitmap. b.UnlockBits(data); } - } /// @@ -819,8 +795,7 @@ public static ICollection FilterGenericTypes(ICollection types) } /// - /// 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. + /// 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) { @@ -851,16 +826,12 @@ public static ICollection CopyDragObjects(ICollection objects, IServiceProvider 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); @@ -872,8 +843,7 @@ public static ICollection CopyDragObjects(ICollection objects, IServiceProvider 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 + // 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) { @@ -884,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); @@ -961,10 +930,8 @@ public static void ApplyTreeViewThemeStyles(TreeView treeView) { throw new ArgumentNullException(nameof(treeView)); } - treeView.HotTracking = true; treeView.ShowLines = false; - IntPtr hwnd = treeView.Handle; SafeNativeMethods.SetWindowTheme(hwnd, "Explorer", null); int exstyle = TreeView_GetExtendedStyle(hwnd); @@ -987,7 +954,6 @@ public static void ApplyListViewThemeStyles(ListView listView) { throw new ArgumentNullException(nameof(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..5d40d301550 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs @@ -0,0 +1,703 @@ +// 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"); + } + 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."); + + Form form = (Form)Control; + form.WindowState = FormWindowState.Normal; + ShadowProperties["AcceptButton"] = form.AcceptButton; + ShadowProperties["CancelButton"] = form.CancelButton; + } + + /// + /// 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..584804cc75c --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IContainsThemedScrollbarWindows.cs @@ -0,0 +1,16 @@ +// 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 +{ + /// + /// 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 index f02f255f501..b472ec881a1 100644 --- 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 @@ -21,5 +21,4 @@ internal interface IMenuStatusHandler /// 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 index a36e6377c24..0bc202dca49 100644 --- 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 @@ -7,10 +7,7 @@ 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 contro. + /// 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 { 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 index 97d946759bb..a07bb34a64c 100644 --- 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 @@ -7,8 +7,7 @@ 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. + /// 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 { @@ -18,27 +17,19 @@ internal interface ISelectionUIService 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. + /// 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. + /// 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. + /// 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); @@ -53,22 +44,17 @@ internal interface ISelectionUIService 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. + /// 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. + /// 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. + /// 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); @@ -88,40 +74,27 @@ internal interface ISelectionUIService 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. + /// 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. + /// 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. + /// 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. + /// 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. + /// 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 index e9f915ee213..5a072d29527 100644 --- 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 @@ -5,8 +5,7 @@ namespace System.Windows.Forms.Design { /// - /// Supports the hosting of several 'pane' windows - /// separated by splitter bars. + /// Supports the hosting of several 'pane' windows separated by splitter bars. /// internal interface ISplitWindowService { 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 index b0db168b5ae..e1ccf59be38 100644 --- 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 @@ -13,9 +13,9 @@ namespace System.Windows.Forms.Design /// internal class InheritanceUI { - private static Bitmap inheritanceGlyph; - private static Rectangle inheritanceGlyphRect; - private ToolTip tooltip; + private static Bitmap s_inheritanceGlyph; + private static Rectangle s_inheritanceGlyphRect; + private ToolTip _tooltip; /// /// The bitmap we use to show inheritance. @@ -24,16 +24,16 @@ public Bitmap InheritanceGlyph { get { - if (inheritanceGlyph == null) + if (s_inheritanceGlyph == null) { - inheritanceGlyph = new Bitmap(typeof(InheritanceUI), "InheritedGlyph.bmp"); - inheritanceGlyph.MakeTransparent(); + s_inheritanceGlyph = new Bitmap(typeof(InheritanceUI), "InheritedGlyph.bmp"); + s_inheritanceGlyph.MakeTransparent(); if (DpiHelper.IsScalingRequired) { - DpiHelper.ScaleBitmapLogicalToDevice(ref inheritanceGlyph); + DpiHelper.ScaleBitmapLogicalToDevice(ref s_inheritanceGlyph); } } - return inheritanceGlyph; + return s_inheritanceGlyph; } } @@ -44,12 +44,12 @@ public Rectangle InheritanceGlyphRectangle { get { - if (inheritanceGlyphRect == Rectangle.Empty) + if (s_inheritanceGlyphRect == Rectangle.Empty) { Size size = InheritanceGlyph.Size; - inheritanceGlyphRect = new Rectangle(0, 0, size.Width, size.Height); + s_inheritanceGlyphRect = new Rectangle(0, 0, size.Width, size.Height); } - return inheritanceGlyphRect; + return s_inheritanceGlyphRect; } } @@ -58,10 +58,12 @@ public Rectangle InheritanceGlyphRectangle /// public void AddInheritedControl(Control c, InheritanceLevel level) { - if (tooltip == null) + if (_tooltip == null) { - tooltip = new ToolTip(); - tooltip.ShowAlways = true; + _tooltip = new ToolTip + { + ShowAlways = true + }; } Debug.Assert(level != InheritanceLevel.NotInherited, "This should only be called for inherited components."); @@ -75,21 +77,23 @@ public void AddInheritedControl(Control c, InheritanceLevel level) text = SR.DesignerInherited; } - tooltip.SetToolTip(c, text); + _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); + _tooltip.SetToolTip(child, text); } } } public void Dispose() { - if (tooltip != null) + if (_tooltip != null) { - tooltip.Dispose(); + _tooltip.Dispose(); } } @@ -98,14 +102,15 @@ public void Dispose() /// public void RemoveInheritedControl(Control c) { - if (tooltip != null && tooltip.GetToolTip(c).Length > 0) + if (_tooltip != null && _tooltip.GetToolTip(c).Length > 0) { - tooltip.SetToolTip(c, null); + _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); + _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..7a16472c30d --- /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); + } + + // 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 d6a82d98650..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 @@ -20,16 +20,10 @@ namespace System.Windows.Forms.Design /// public class ParentControlDesigner : ControlDesigner, IOleDragClient { - private IComponentChangeService componentChangeSvc; - private Control pendingRemoveControl; // we've gotten an OnComponentRemoving, and are waiting for OnComponentRemove - private bool checkSnapLineSetting = true; // Since layout options is global for the duration of the designer, we should only query it once. - private bool defaultUseSnapLines = false; - private bool gridSnap = true; - private bool getDefaultGridSnap = true; - private bool parentCanSetGridSnap = true; // been set explicitly by a user - so to ignore the parent's setting - + 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; @@ -108,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) @@ -262,178 +506,64 @@ 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. /// protected Rectangle GetUpdatedRect(Rectangle originalRect, Rectangle dragRect, bool updateSize) { - Rectangle updatedRect = Rectangle.Empty;//the rectangle with updated coords that we will return - - if (SnapToGrid) - { - Size gridSize = GridSize; - Point halfGrid = new Point(gridSize.Width / 2, gridSize.Height / 2); - - updatedRect = dragRect; - updatedRect.X = originalRect.X; - updatedRect.Y = originalRect.Y; - - // decide to snap the start location to grid ... - if (dragRect.X != originalRect.X) - { - updatedRect.X = (dragRect.X / gridSize.Width) * gridSize.Width; - - // Snap the location to the grid point closest to the dragRect location - if (dragRect.X - updatedRect.X > halfGrid.X) - { - updatedRect.X += gridSize.Width; - } - } - - if (dragRect.Y != originalRect.Y) - { - updatedRect.Y = (dragRect.Y / gridSize.Height) * gridSize.Height; - - // Snap the location to the grid point closest to the dragRect location - if (dragRect.Y - updatedRect.Y > halfGrid.Y) - { - updatedRect.Y += gridSize.Height; - } - } - - // here, we need to calculate the new size depending on how we snap to the grid ... - if (updateSize) - { - // update the width and the height - updatedRect.Width = ((dragRect.X + dragRect.Width) / gridSize.Width) * gridSize.Width - updatedRect.X; - updatedRect.Height = ((dragRect.Y + dragRect.Height) / gridSize.Height) * gridSize.Height - updatedRect.Y; - - // ASURT 71552 Added so that if the updated dimnesion is smaller than grid dimension then snap that dimension to the grid dimension - if (updatedRect.Width < gridSize.Width) - updatedRect.Width = gridSize.Width; - if (updatedRect.Height < gridSize.Height) - updatedRect.Height = gridSize.Height; - } - } - else - { - updatedRect = dragRect; - } - - return updatedRect; + throw new NotImplementedException(SR.NotImplementedByDesign); } - private bool DefaultUseSnapLines + /// + /// 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) { - get + base.Initialize(component); + if (Control is ScrollableControl) { - if (checkSnapLineSetting) - { - checkSnapLineSetting = false; - defaultUseSnapLines = DesignerUtils.UseSnapLines(Component.Site); - } - return defaultUseSnapLines; + ((ScrollableControl)Control).Scroll += new ScrollEventHandler(OnScroll); } - } - - private ParentControlDesigner GetParentControlDesignerOfParent() - { - Control parent = Control.Parent; + EnableDragDrop(true); IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); - if (parent != null && host != null) + if (host != null) { - return (host.GetDesigner(parent) as ParentControlDesigner); + componentChangeSvc = (IComponentChangeService)host.GetService(typeof(IComponentChangeService)); } - return null; + // update the Status Command + statusCommandUI = new StatusCommandUI(component.Site); } - private IServiceProvider ServiceProvider + private void OnComponentRemoved(object sender, ComponentEventArgs e) { - get + if (e.Component == pendingRemoveControl) { - if (Component != null) - { - return Component.Site; - } - - return null; + pendingRemoveControl = null; + componentChangeSvc.OnComponentChanged(Control, TypeDescriptor.GetProperties(Control)["Controls"], null, null); } } - private bool SnapToGrid + private void OnComponentRemoving(object sender, ComponentEventArgs e) { - get + if (e.Component is Control comp && comp.Parent != null && comp.Parent == Control) { - // If snaplines are on, the we never want to snap to grid - if (DefaultUseSnapLines) + pendingRemoveControl = (Control)comp; + //We suspend Component Changing Events for bulk operations to avoid unnecessary serialization\deserialization for undo + if (suspendChanging == 0) { - return false; - } - else if (getDefaultGridSnap) - { - gridSnap = true; - //Before we check our options page, we need to see if our parent is a ParentControlDesigner, is so, then we will want to inherit all our grid/snap setting from it - instead of our options page - ParentControlDesigner parent = GetParentControlDesignerOfParent(); - if (parent != null) - { - gridSnap = parent.SnapToGrid; - } - else - { - object optionValue = DesignerUtils.GetOptionValue(ServiceProvider, "SnapToGrid"); - if (optionValue != null && optionValue is bool) - { - gridSnap = (bool)optionValue; - } - } - } - return gridSnap; - } - set - { - if (gridSnap != value) - { - if (parentCanSetGridSnap) - { - parentCanSetGridSnap = false; - } - - if (getDefaultGridSnap) - { - getDefaultGridSnap = false; - } - gridSnap = value; - - //now, notify all child parent control designers that we have changed our setting 'cause they might to change along with us, unless the user has explicitly set those values... - IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); - if (host != null) - { - foreach (Control child in Control.Controls) - { - if (host.GetDesigner(child) is ParentControlDesigner designer) - { - designer.GridSnapOfParentChanged(gridSnap); - } - } - } - + componentChangeSvc.OnComponentChanging(Control, TypeDescriptor.GetProperties(Control)["Controls"]); } } } - private void GridSnapOfParentChanged(bool gridSnap) - { - if (parentCanSetGridSnap) - { - // If the parent sets us, then treat this as if no one set us - bool getDefaultGridSnapTemp = getDefaultGridSnap; - SnapToGrid = gridSnap; - parentCanSetGridSnap = true; - getDefaultGridSnap = getDefaultGridSnapTemp; - } - } - internal void SuspendChangingEvents() { suspendChanging++; @@ -444,63 +574,13 @@ internal void ForceComponentChanging() { componentChangeSvc.OnComponentChanging(Control, TypeDescriptor.GetProperties(Control)["Controls"]); } + internal void ResumeChangingEvents() { suspendChanging--; Debug.Assert(suspendChanging >= 0, "Unbalanced SuspendChangingEvents\\ResumeChangingEvents"); } - /// - /// 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) - { - base.Initialize(component); - if (Control is ScrollableControl) - { - ((ScrollableControl)Control).Scroll += new ScrollEventHandler(this.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 OnComponentRemoving(object sender, ComponentEventArgs e) - { - using (Control comp = e.Component as Control) - { - if (comp != null && comp.Parent != null && comp.Parent == Control) - { - pendingRemoveControl = (Control)comp; - //We suspend Component Changing Events for bulk operations to avoid unnecessary serialization\deserialization for undo see bug 488115 - if (suspendChanging == 0) - { - componentChangeSvc.OnComponentChanging(Control, TypeDescriptor.GetProperties(Control)["Controls"]); - } - } - } - } - private void OnComponentRemoved(object sender, ComponentEventArgs e) - { - if (e.Component == pendingRemoveControl) - { - pendingRemoveControl = null; - componentChangeSvc.OnComponentChanged(Control, TypeDescriptor.GetProperties(Control)["Controls"], null, null); - } - } - /// /// public override void InitializeNewComponent(IDictionary defaultValues) @@ -622,11 +702,5 @@ protected override void PreFilterProperties(IDictionary properties) { 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); - } } } 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..3b665359cbf --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionManager.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; +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"); + } + + _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(); + // 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 = null; + } + if (_serviceProvider != null) + { + if (_selSvc != null) + { + _selSvc = null; + } + _serviceProvider = null; + } + if (_behaviorService != null) + { + _behaviorService.Adorners.Remove(_bodyAdorner); + _behaviorService.Adorners.Remove(_selectionAdorner); + _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 index 1687e87abcb..03d66484741 100644 --- 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 @@ -14,15 +14,14 @@ 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. + /// 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 index f1ac31dbd9d..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 @@ -13,9 +13,7 @@ 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. + /// 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 { @@ -23,13 +21,13 @@ internal sealed class SelectionUIService : Control, ISelectionUIService 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 @@ -38,7 +36,6 @@ internal sealed class SelectionUIService : Control, ISelectionUIService 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 @@ -64,9 +61,9 @@ public SelectionUIService(IDesignerHost host) : base() _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) { @@ -146,6 +143,7 @@ protected override void Dispose(bool disposing) { _selSvc.SelectionChanged -= new EventHandler(OnSelectionChanged); } + if (_host != null) { _host.TransactionOpened -= new EventHandler(OnTransactionOpened); @@ -155,6 +153,7 @@ protected override void Dispose(bool disposing) OnTransactionClosed(_host, new DesignerTransactionCloseEventArgs(true, true)); } } + foreach (SelectionUIItem s in _selectionItems.Values) { s.Dispose(); @@ -179,7 +178,6 @@ private void EndMouseDrag(Point position) { return; } - _ignoreCaptureChanged = true; Capture = false; _mouseDragAnchor = s_invalidPoint; @@ -291,7 +289,6 @@ public static string GetTransactionName(SelectionRules rules, object[] objects) { transactionName = string.Format(SR.DragDropDragComponents, objects.Length); } - return transactionName; } @@ -317,7 +314,7 @@ private void OnTransactionClosed(object sender, DesignerTransactionCloseEventArg } /// - /// Called by the designer host when it is entering or leaving a batch operation. Here we queue up selection notification and we turn offour UI. + /// 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) { @@ -554,6 +551,7 @@ protected override void OnMouseDown(MouseEventArgs me) { rules |= SelectionRules.BottomSizeable; } + if (((ISelectionUIService)this).BeginDrag(rules, anchor.X, anchor.Y)) { BeginMouseDrag(anchor, hitTest); @@ -600,11 +598,11 @@ protected override void OnMouseMove(MouseEventArgs me) { OnContainerSelectorActive(new ContainerSelectorActiveEventArgs(hti.selectionUIHit._component)); } + if (_lastMoveScreenCoord == screenCoord) { return; } - // If we're not dragging then set the cursor correctly. if (!_mouseDragging) { @@ -679,12 +677,10 @@ protected override void OnMouseMove(MouseEventArgs me) 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) + if ((_mouseDragHitTest & SelectionUIItem.MOVE_X) != 0 || (_mouseDragHitTest & SelectionUIItem.MOVE_Y) != 0) { Cursor = Cursors.Default; } @@ -710,6 +706,7 @@ protected override void OnMouseUp(MouseEventArgs me) HitTestInfo hti = GetHitTest(screenCoord, HITTEST_DEFAULT); _selSvc.SetSelectedComponents(new object[] { hti.selectionUIHit._component }, SelectionTypes.Primary); } + if (_mouseDragging) { object oldContainerDrag = _containerDrag; @@ -719,6 +716,7 @@ protected override void OnMouseUp(MouseEventArgs me) { ((ISelectionUIService)this).EndDrag(false); } + if (me.Button == MouseButtons.Right && oldContainerDrag != null && !oldDragMoved) { OnContainerSelectorActive(new ContainerSelectorActiveEventArgs(oldContainerDrag, ContainerSelectorActiveEventArgsType.Contextmenu)); @@ -832,6 +830,7 @@ private void UpdateWindowRegion() { region.Union(item.GetRegion()); } + Region = region; } @@ -849,7 +848,6 @@ protected override void WndProc(ref Message m) _ignoreCaptureChanged = true; } break; - case NativeMethods.WM_CAPTURECHANGED: if (!_ignoreCaptureChanged && _mouseDragAnchor != s_invalidPoint) { @@ -887,26 +885,19 @@ bool ISelectionUIService.Visible /// event ContainerSelectorActiveEventHandler ISelectionUIService.ContainerSelectorActive { - add - { - _containerSelectorActive += value; - } - remove - { - _containerSelectorActive -= value; - } + 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. + /// 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) { - // ASURT #44582: 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. + // 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; @@ -1005,7 +996,6 @@ bool ISelectionUIService.BeginDrag(SelectionRules rules, int initialX, int initi _dragComponents = objects; _dragRules = rules; _dragHandler = primaryHandler; - string transactionName = GetTransactionName(rules, objects); _dragTransaction = _host.CreateTransaction(transactionName); try @@ -1028,11 +1018,13 @@ bool ISelectionUIService.BeginDrag(SelectionRules rules, int initialX, int initi } 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) { @@ -1093,7 +1085,6 @@ 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; @@ -1107,7 +1098,6 @@ void ISelectionUIService.EndDrag(bool cancel) DesignerTransaction trans = null; try { - IComponent comp = components[0] as IComponent; if (components.Length > 1 || (components.Length == 1 && comp != null && comp.Site == null)) { @@ -1120,7 +1110,6 @@ void ISelectionUIService.EndDrag(bool cancel) trans = _host.CreateTransaction(string.Format(SR.DragDropMoveComponent, comp.Site.Name)); } } - try { handler.EndDrag(components, cancel); @@ -1155,7 +1144,6 @@ object[] ISelectionUIService.FilterSelection(object[] components, 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) { @@ -1195,14 +1183,7 @@ Size ISelectionUIService.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 ISelectionUIService.GetAdornmentHitTest(object component, Point value) - { - if (GetHitTest(value, HITTEST_DEFAULT).hitTest != SelectionUIItem.NOHIT) - { - return true; - } - return false; - } + 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. @@ -1252,9 +1233,7 @@ void ISelectionUIService.SetContainerSelected(object component, bool selected) } SelectionUIItem item = new ContainerSelectionUIItem(this, component); _selectionItems[component] = item; - // Now update our region and invalidate - // UpdateWindowRegion(); if (existingItem != null) { @@ -1318,7 +1297,6 @@ void ISelectionUIService.SyncSelection() updateRegion |= item.UpdateSize(); item.UpdateRules(); } - if (updateRegion) { UpdateWindowRegion(); @@ -1459,9 +1437,9 @@ public virtual SelectionStyles Style 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) { @@ -1566,7 +1544,6 @@ 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) { @@ -1575,7 +1552,6 @@ public virtual int GetHitTest(Point pt) return _sizes[nOffset]; } - /// /// gets the array offset of the handle at the given point /// @@ -1585,13 +1561,13 @@ private int GetHandleIndexOfPoint(Point pt) { // Something on the left side. if (pt.Y >= _outerRect.Y && pt.Y <= _innerRect.Y) - return 0; // top left + return 0; // top left if (pt.Y >= _innerRect.Y + _innerRect.Height && pt.Y <= _outerRect.Y + _outerRect.Height) - return 5; // bottom left + 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 + return 3; // middle left + return -1; // unknown hit } if (pt.Y >= _outerRect.Y && pt.Y <= _innerRect.Y) @@ -1599,11 +1575,11 @@ private int GetHandleIndexOfPoint(Point pt) // 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 + 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 + return 1; // top middle + return -1; // unknown hit } if (pt.X >= _innerRect.X + _innerRect.Width && pt.X <= _outerRect.X + _outerRect.Width) @@ -1611,21 +1587,23 @@ private int GetHandleIndexOfPoint(Point pt) // 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 + 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 + 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 6; // bottom middle + return -1; // unknown hit } return -1; // unknown hit } @@ -1694,17 +1672,14 @@ protected bool PointWithinSelection(Point pt) { 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 @@ -1803,16 +1778,11 @@ public virtual bool UpdateSize() 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); + 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) @@ -1864,6 +1834,7 @@ public override int GetHitTest(Point pt) 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; @@ -1945,9 +1916,7 @@ public override bool Equals(object obj) public static bool operator ==(HitTestInfo left, HitTestInfo right) { - return (left.hitTest == right.hitTest - && left.selectionUIHit == right.selectionUIHit - && left.containerSelector == right.containerSelector); + return (left.hitTest == right.hitTest && left.selectionUIHit == right.selectionUIHit && left.containerSelector == right.containerSelector); } public static bool operator !=(HitTestInfo left, HitTestInfo right) => !(left == right); 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..614d2ce5a32 --- /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 Icon(typeof(ToolStripMenuItem), _name + ".bmp").ToBitmap(); + } + 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/ThemedScrollbarMode.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ThemedScrollbarMode.cs index 7b0428638f1..0db15f48949 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ThemedScrollbarMode.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ThemedScrollbarMode.cs @@ -28,14 +28,5 @@ 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/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..01ff504fd46 --- /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 |= NativeMethods.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..335ad55c2a3 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripDesigner.cs @@ -0,0 +1,2551 @@ +// 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 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 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 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 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 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 + { + 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 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 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) + { + InitializeNewItemDropDown(); + } + + /// + /// 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) + { + // 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 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) + { + 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 (_selectionSvc != null) + { + _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; + } + + // 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 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 ToolStrip... + 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)); + } + + // initialize new Manager For Editing ToolStrips + _editManager = new ToolStripEditorManager(component); + // setup the dropdown if our handle has been created. + if (Control.IsHandleCreated) + { + InitializeNewItemDropDown(); + } + + //hookup to the AdornerService..for the overflow dropdown to be parent properly. + _toolStripAdornerWindowService = (ToolStripAdornerWindowService)GetService(typeof(ToolStripAdornerWindowService)); + + // 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 ToolStrip. + 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.SuspendLayout(); + ToolStrip.Items.Add(_editorNode); + ToolStrip.ResumeLayout(); + } + } + 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 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 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 ToolStrip, 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 ToolStrip, 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 + { + FireSyncSelection = true; + _editorNode.Visible = true; + } + finally + { + FireSyncSelection = originalSyncSelection; + } + } + + // 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..c340dd338d3 --- /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 ToolStrip 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..fce793214cc --- /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) + { + // 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..3157e67ff23 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripItemCustomMenuItemCollection.cs @@ -0,0 +1,700 @@ +// 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 Icon(typeof(ToolStripMenuItem), imageName).ToBitmap(); + 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 Icon(typeof(ToolStripMenuItem), "editdropdownlist.bmp").ToBitmap(); + 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..4bbb8acf1d1 --- /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 ToolStripItemBehavior. + 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..e26e8879848 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripKeyboardHandlingService.cs @@ -0,0 +1,2131 @@ +// 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 ToolStrip 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 ToolStripItem. + 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..554b23346da --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripMenuItemDesigner.cs @@ -0,0 +1,2773 @@ +// 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; + } + + 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; + + 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 ToolStrip 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..71b1d2a1257 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripTemplateNode.cs @@ -0,0 +1,2045 @@ +// 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 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 + { + 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[] { }; + _addCommands = new MenuCommand[] { }; + } + + /// + /// 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 + { + 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 ToolStrip to a TransparentToolStrip. 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.Design/tests/UnitTests/ControlDesignerTests.cs b/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs index e4fa0febbd4..51568c546ab 100644 --- a/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs +++ b/src/System.Windows.Forms.Design/tests/UnitTests/ControlDesignerTests.cs @@ -9,212 +9,213 @@ namespace System.Windows.Forms.Design.Tests { public class ControlDesignerTests { - internal MockControlDesigner controlDesigner = new MockControlDesigner(); - [Fact] public void AccessibleObjectField() { + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.Null(controlDesigner.GetAccessibleObjectField()); } [Fact] public void BehaviorServiceProperty() { - Assert.Throws(() => controlDesigner.GetBehaviorServiceProperty()); - } - - [Fact] - public void AssociatedComponentsProperty() - { - Assert.Throws(() => controlDesigner.AssociatedComponents); + TestControlDesigner controlDesigner = new TestControlDesigner(); + Assert.Null(controlDesigner.GetBehaviorServiceProperty()); } [Fact] public void AccessibilityObjectField() { - Assert.Throws(() => controlDesigner.AccessibilityObject); - } - - [Fact] - public void ControlProperty() - { - Assert.Throws(() => controlDesigner.Control); + TestControlDesigner controlDesigner = new TestControlDesigner(); + Assert.NotNull(controlDesigner.AccessibilityObject); } [Fact] public void EnableDragRectProperty() { - Assert.Throws(() => controlDesigner.GetEnableDragRectProperty()); - } - - [Fact] - public void ParentComponentProperty() - { - Assert.Throws(() => controlDesigner.GetParentComponentProperty()); + TestControlDesigner controlDesigner = new TestControlDesigner(); + Assert.False(controlDesigner.GetEnableDragRectProperty()); } [Fact] public void ParticipatesWithSnapLinesProperty() { - Assert.Throws(() => controlDesigner.ParticipatesWithSnapLines); + TestControlDesigner controlDesigner = new TestControlDesigner(); + Assert.True(controlDesigner.ParticipatesWithSnapLines); } [Fact] public void AutoResizeHandlesProperty() { - Assert.Throws(() => controlDesigner.AutoResizeHandles = true); - Assert.Throws(() => controlDesigner.AutoResizeHandles); + TestControlDesigner controlDesigner = new TestControlDesigner(); + 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); + TestControlDesigner controlDesigner = new TestControlDesigner(); + Assert.Equal(SelectionRules.Visible ,controlDesigner.SelectionRules); } [Fact] public void InheritanceAttributeProperty() { - Assert.Throws(() => controlDesigner.GetInheritanceAttributeProperty()); + TestControlDesigner controlDesigner = new TestControlDesigner(); + Assert.NotNull(controlDesigner); + controlDesigner.Initialize(new Button()); + Assert.NotNull(controlDesigner.GetInheritanceAttributeProperty()); } [Fact] public void NumberOfInternalControlDesignersTest() { - Assert.Throws(() => controlDesigner.NumberOfInternalControlDesigners()); - } - - [Fact] - public void InternalControlDesignerTest() - { - Assert.Throws(() => controlDesigner.InternalControlDesigner(1)); + TestControlDesigner controlDesigner = new TestControlDesigner(); + Assert.Equal(0, controlDesigner.NumberOfInternalControlDesigners()); } [Fact] public void BaseWndProcTest() { + TestControlDesigner controlDesigner = new TestControlDesigner(); 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())); - } - - [Fact] - public void DefWndProcTest() - { - Message m = default; - Assert.Throws(() => controlDesigner.DefWndProcMethod(ref m)); + TestControlDesigner controlDesigner = new TestControlDesigner(); + Assert.NotNull(controlDesigner); + controlDesigner.Initialize(new Button()); + Assert.True(controlDesigner.CanBeParentedTo(new ParentControlDesigner())); } - [Fact] - public void DisplayErrorTest() - { - Assert.Throws(() => controlDesigner.DisplayErrorMethod(new Exception())); - } - - /// - /// Bool data - /// public static TheoryData BoolData => CommonTestHelper.GetBoolTheoryData(); - [Theory] - [MemberData(nameof(BoolData))] - public void DisposeTest(bool val) - { - Assert.Throws(() => controlDesigner.DisposeMethod(val)); - } - - [Fact] - public void EnableDesignModeTest() - { - Assert.Throws(() => controlDesigner.EnableDesignModeMethod(null, "fake")); - } - [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)); + TestControlDesigner controlDesigner = new TestControlDesigner(); + try + { + controlDesigner.EnableDragDropMethod(val); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } [Fact] public void GetHitTest() { + TestControlDesigner controlDesigner = new TestControlDesigner(); Assert.False(controlDesigner.GetHitTestMethod(new Drawing.Point())); } [Fact] public void HookChildControlsTest() { - Assert.Throws(() => controlDesigner.HookChildControlsMethod(null)); - } + TestControlDesigner controlDesigner = new TestControlDesigner(); + Assert.NotNull(controlDesigner); + controlDesigner.Initialize(new Button()); + try + { - [Fact] - public void InitializeTest() - { - Assert.Throws(() => controlDesigner.Initialize(null)); + controlDesigner.HookChildControlsMethod(new Control()); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } [Fact] - public void InitializeExistingComponentTest() + public void InitializeTest() { - Assert.Throws(() => controlDesigner.InitializeExistingComponent(null)); + TestControlDesigner controlDesigner = new TestControlDesigner(); + try + { + controlDesigner.Initialize(new Button()); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } [Fact] public void InitializeNewComponentTest() { - Assert.Throws(() => controlDesigner.InitializeNewComponent(null)); + TestControlDesigner controlDesigner = new TestControlDesigner(); + 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() { + TestControlDesigner controlDesigner = new TestControlDesigner(); #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))); + TestControlDesigner controlDesigner = new TestControlDesigner(); + 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()); + TestControlDesigner controlDesigner = new TestControlDesigner(); + Assert.NotNull(controlDesigner); + controlDesigner.Initialize(new Button()); + try + { + controlDesigner.OnCreateHandleMethod(); + } + catch (Exception ex) + { + Assert.True(false, "Expected no exception, but got: " + ex.Message); + } } } } 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() { diff --git a/src/System.Windows.Forms/src/BitmapSelector.cs b/src/System.Windows.Forms/src/BitmapSelector.cs deleted file mode 100644 index db468642757..00000000000 --- a/src/System.Windows.Forms/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/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/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/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; 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(); 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 {