Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / Designer / CompMod / System / ComponentModel / Design / DesignerActionPanel.cs / 5 / DesignerActionPanel.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- // IMPORTANT NOTE ([....]): // This file is published by the build system to public\internal\ndp\inc so that // its contents can be built into Microsoft.VisualStudio.Web.dll. As such, when // making modifications to the file always make sure to build clean in the // venus\mvw folder to make sure you do not cause a build break. // The original location of this file is: // ndp\fx\src\Designer\CompMod\System\ComponentModel\Design\DesignerActionPanel.cs // and that is where all changes should be made. #if MVWASSEMBLY namespace Microsoft.VisualStudio.Web { #else namespace System.ComponentModel.Design { #endif using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.ComponentModel.Design; 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; #if !MVWASSEMBLY using System.Design; #endif using ContentAlignment = System.Drawing.ContentAlignment; internal sealed class DesignerActionPanel : ContainerControl { public const string ExternDllGdi32 = "gdi32.dll"; public const string ExternDllUser32 = "user32.dll"; private static readonly object EventFormActivated = new object(); private static readonly object 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 ToolTip _toolTip; private List_lines; private List _lineYPositions; private List _lineHeights; private Color _gradientLightColor = SystemColors.Control; private Color _gradientDarkColor = SystemColors.Control; private Color _titleBarColor = SystemColors.ActiveCaption; private Color _titleBarUnselectedColor = SystemColors.InactiveCaption; private Color _titleBarTextColor = SystemColors.ActiveCaptionText; private Color _separatorColor = SystemColors.ControlDark; private Color _borderColor = SystemColors.ActiveBorder; private Color _linkColor = SystemColors.HotTrack; private Color _activeLinkColor = SystemColors.HotTrack; private IServiceProvider _serviceProvider; private bool _inMethodInvoke; #if MVWASSEMBLY private bool _inPushingValue; #endif private bool _updatingTasks; private bool _dropDownActive; public DesignerActionPanel(IServiceProvider serviceProvider) { SetStyle(ControlStyles.AllPaintingInWmPaint, true); SetStyle(ControlStyles.Opaque, true); SetStyle(ControlStyles.OptimizedDoubleBuffer, true); SetStyle(ControlStyles.ResizeRedraw, true); SetStyle(ControlStyles.UserPaint, true); _serviceProvider = serviceProvider; _lines = new List (); _lineHeights = new List (); _lineYPositions = new List (); _toolTip = new ToolTip(); // Try to get the font from the IUIService, otherwise, use the default IUIService uiService = (IUIService)ServiceProvider.GetService(typeof(IUIService)); if (uiService != null) { Font = (Font)uiService.Styles["DialogFont"]; if (uiService.Styles["VsColorPanelGradientDark"] is Color) { _gradientDarkColor = (Color)uiService.Styles["VsColorPanelGradientDark"]; } if (uiService.Styles["VsColorPanelGradientLight"] is Color) { _gradientLightColor = (Color)uiService.Styles["VsColorPanelGradientLight"]; } if (uiService.Styles["VsColorPanelHyperLink"] is Color) { _linkColor = (Color)uiService.Styles["VsColorPanelHyperLink"]; } if (uiService.Styles["VsColorPanelHyperLinkPressed"] is Color) { _activeLinkColor = (Color)uiService.Styles["VsColorPanelHyperLinkPressed"]; } if (uiService.Styles["VsColorPanelTitleBar"] is Color) { _titleBarColor = (Color)uiService.Styles["VsColorPanelTitleBar"]; } if (uiService.Styles["VsColorPanelTitleBarUnselected"] is Color) { _titleBarUnselectedColor = (Color)uiService.Styles["VsColorPanelTitleBarUnselected"]; } if (uiService.Styles["VsColorPanelTitleBarText"] is Color) { _titleBarTextColor = (Color)uiService.Styles["VsColorPanelTitleBarText"]; } if (uiService.Styles["VsColorPanelBorder"] is Color) { _borderColor = (Color)uiService.Styles["VsColorPanelBorder"]; } if (uiService.Styles["VsColorPanelSeparator"] is Color) { _separatorColor = (Color)uiService.Styles["VsColorPanelSeparator"]; } } MinimumSize = new Size(150, 0); } public Color ActiveLinkColor { get { return _activeLinkColor; } } public Color BorderColor { get { return _borderColor; } } private bool DropDownActive { get { return _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 { return _gradientDarkColor; } } public Color GradientLightColor { get { return _gradientLightColor; } } public bool InMethodInvoke { get { return _inMethodInvoke; } internal set { _inMethodInvoke = value; } } #if MVWASSEMBLY public bool InPushingValue { get { return _inPushingValue; } internal set { _inPushingValue = value; } } #endif public Color LinkColor { get { return _linkColor; } } public Color SeparatorColor { get { return _separatorColor; } } private IServiceProvider ServiceProvider { get { return _serviceProvider; } } public Color TitleBarColor { get { return _titleBarColor; } } public Color TitleBarTextColor { get { return _titleBarTextColor; } } public Color TitleBarUnselectedColor { get { return _titleBarUnselectedColor; } } ////// Helper event so that Lines can be notified of this event. /// private event EventHandler FormActivated { add { Events.AddHandler(EventFormActivated, value); } remove { Events.RemoveHandler(EventFormActivated, value); } } ////// Helper event so that Lines can be notified of this event. /// private event EventHandler FormDeactivate { add { Events.AddHandler(EventFormDeactivate, value); } remove { Events.RemoveHandler(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); } ListcategoryList = (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) { // 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) { // 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(); // } private void OnFormActivated(object sender, EventArgs e) { EventHandler handler = (EventHandler)Events[EventFormActivated]; if (handler != null) { handler(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 handler = (EventHandler)Events[EventFormDeactivate]; if (handler != null) { handler(sender, e); } } protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); Form form = TopLevelControl as Form; if (form != null) { 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; DesignerActionPropertyItem propItem = item as DesignerActionPropertyItem; if (propItem != null) { relatedComponent = propItem.RelatedComponent; } else { DesignerActionMethodItem methodItem = item as DesignerActionMethodItem; if (methodItem != null) { relatedComponent = methodItem.RelatedComponent; } } if (relatedComponent != null) { IEnumerablerelatedLineInfos = 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) { ComponentDesigner componentDesigner = host.GetDesigner(relatedComponent) as ComponentDesigner; if (componentDesigner != null) { relatedLists = componentDesigner.ActionLists; } } } List lineInfos = new List (); if (relatedLists != null) { foreach (DesignerActionList relatedList in relatedLists) { if (relatedList != null) { Type relatedListType = relatedList.GetType(); 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 = null; if (item is DesignerActionMethodItem) { newLine = new MethodLine(_serviceProvider, this); } else if (item is DesignerActionPropertyItem) { DesignerActionPropertyItem pti = (DesignerActionPropertyItem)item; PropertyDescriptor pd = TypeDescriptor.GetProperties(list)[pti.MemberName]; if (pd == null) { throw new InvalidOperationException(SR.GetString(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.GetString(SR.RTL) != "RTL_False") { options = (MessageBoxOptions.RightAlign | MessageBoxOptions.RtlReading); } MessageBox.Show(this, errorMessage, SR.GetString(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++) { TextBoxPropertyLine line = _lines[i] as TextBoxPropertyLine; if (line != null) { editXPos = Math.Max(editXPos, ((TextBoxPropertyLine)line).GetEditRegionXPos()); } } // Make all the edit controls line up for (int i = 0; i < _lines.Count; i++) { TextBoxPropertyLine line = _lines[i] as TextBoxPropertyLine; if (line != null) { 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 ListnewLines = new List (); // Always add a special line for the header newLines.Add(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; // // 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 IServiceProvider _serviceProvider; private PropertyDescriptor _propDesc; private object _instance; public TypeDescriptorContext(IServiceProvider serviceProvider, PropertyDescriptor propDesc, object instance) { _serviceProvider = serviceProvider; _propDesc = propDesc; _instance = instance; } private IComponentChangeService ComponentChangeService { get { return (IComponentChangeService)_serviceProvider.GetService(typeof(IComponentChangeService)); } } public IContainer Container { get { return (IContainer)_serviceProvider.GetService(typeof(IContainer)); } } public object Instance { get { return _instance; } } public PropertyDescriptor PropertyDescriptor { get { return _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 DesignerActionPanel _actionPanel; private List _addedControls; private IServiceProvider _serviceProvider; public Line(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) { if (actionPanel == null) { throw new ArgumentNullException("actionPanel"); } _serviceProvider = serviceProvider; _actionPanel = actionPanel; } protected DesignerActionPanel ActionPanel { get { return _actionPanel; } } public abstract string FocusId { get; } protected IServiceProvider ServiceProvider { get { return _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 string _subtitle; public DesignerActionPanelHeaderItem(string title, string subtitle) : base(title, null, null) { _subtitle = subtitle; } public string Subtitle { get { return _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 { return String.Empty; } } protected override void AddControls(List controls) { _titleLabel = new Label(); _titleLabel.BackColor = Color.Transparent; _titleLabel.ForeColor = ActionPanel.TitleBarTextColor; _titleLabel.TextAlign = ContentAlignment.MiddleLeft; _titleLabel.UseMnemonic = false; _subtitleLabel = new Label(); _subtitleLabel.BackColor = Color.Transparent; _subtitleLabel.ForeColor = ActionPanel.TitleBarTextColor; _subtitleLabel.TextAlign = ContentAlignment.MiddleLeft; _subtitleLabel.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(Int32.MaxValue, Int32.MaxValue)); Size subtitleSize = Size.Empty; if (!String.IsNullOrEmpty(_panelHeaderItem.Subtitle)) { subtitleSize = _subtitleLabel.GetPreferredSize(new Size(Int32.MaxValue, Int32.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(); //ActionPanel.Invalidate(new Rectangle(EditRegionLocation, EditRegionSize), false); } 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) ? backColor = 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 { return "METHOD:" + _actionList.GetType().FullName + "." + _methodItem.MemberName; } } protected override void AddControls(List controls) { _linkLabel = new MethodItemLinkLabel(); _linkLabel.ActiveLinkColor = ActionPanel.ActiveLinkColor; _linkLabel.AutoSize = false; _linkLabel.BackColor = Color.Transparent; _linkLabel.LinkBehavior = LinkBehavior.HoverUnderline; _linkLabel.LinkColor = ActionPanel.LinkColor; _linkLabel.TextAlign = ContentAlignment.MiddleLeft; _linkLabel.UseMnemonic = false; _linkLabel.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(Int32.MaxValue, Int32.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(SR.GetString(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 { return "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 { return _propertyItem; } } protected ITypeDescriptorContext TypeDescriptorContext { get { if (_typeDescriptorContext == null) { _typeDescriptorContext = new TypeDescriptorContext(ServiceProvider, PropertyDescriptor, _actionList); } return _typeDescriptorContext; } } protected object Value { get { return _value; } } protected abstract void OnPropertyTaskItemUpdated(ToolTip toolTip, ref int currentTabIndex); protected abstract void OnValueChanged(); [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] protected void SetValue(object newValue) { if (_pushingValue || ActionPanel.DropDownActive) { return; } _pushingValue = true; #if MVWASSEMBLY ActionPanel.InPushingValue = true; #endif try { // Only push the change if the values are different if (newValue != null) { Type valueType = newValue.GetType(); // If it's not assignable, try to convert it if (!PropertyDescriptor.PropertyType.IsAssignableFrom(valueType)) { if (PropertyDescriptor.Converter != null) { // If we can't convert it, show an error if (!PropertyDescriptor.Converter.CanConvertFrom(_typeDescriptorContext, valueType)) { ActionPanel.ShowError(SR.GetString(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(SR.GetString(SR.DesignerActionPanel_ErrorSettingValue, newValue, PropertyDescriptor.Name, e.Message)); } catch { //Not a critical exception } finally { _pushingValue = false; #if MVWASSEMBLY ActionPanel.InPushingValue = false; #endif } } internal sealed override void UpdateActionItem(DesignerActionList actionList, DesignerActionItem actionItem, ToolTip toolTip, ref int currentTabIndex) { _actionList = actionList; _propertyItem = (DesignerActionPropertyItem)actionItem; _propDesc = null; _typeDescriptorContext = null; _value = PropertyDescriptor.GetValue(actionList); OnPropertyTaskItemUpdated(toolTip, ref currentTabIndex); _pushingValue = true; #if MVWASSEMBLY ActionPanel.InPushingValue = true; #endif try { OnValueChanged(); } finally { _pushingValue = false; #if MVWASSEMBLY ActionPanel.InPushingValue = false; #endif } } } private sealed class CheckBoxPropertyLine : PropertyLine { private CheckBox _checkBox; public CheckBoxPropertyLine(IServiceProvider serviceProvider, DesignerActionPanel actionPanel) : base(serviceProvider, actionPanel) { } protected override void AddControls(List controls) { _checkBox = new CheckBox(); _checkBox.BackColor = Color.Transparent; _checkBox.CheckAlign = ContentAlignment.MiddleLeft; _checkBox.CheckedChanged += new EventHandler(OnCheckBoxCheckedChanged); _checkBox.TextAlign = ContentAlignment.MiddleLeft; _checkBox.UseMnemonic = false; 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(Int32.MaxValue, Int32.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 { return _editControl; } } protected Point EditRegionLocation { get { return _editRegionLocation; } } protected Point EditRegionRelativeLocation { get { return _editRegionRelativeLocation; } } protected Size EditRegionSize { get { return _editRegionSize; } } protected override void AddControls(List controls) { _label = new Label(); _label.BackColor = Color.Transparent; _label.TextAlign = ContentAlignment.MiddleLeft; _label.UseMnemonic = false; _readOnlyTextBoxLabel = new EditorLabel(); _readOnlyTextBoxLabel.BackColor = Color.Transparent; _readOnlyTextBoxLabel.TabStop = true; _readOnlyTextBoxLabel.TextAlign = ContentAlignment.TopLeft; _readOnlyTextBoxLabel.UseMnemonic = false; _readOnlyTextBoxLabel.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(); _textBox.BorderStyle = BorderStyle.None; _textBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Left; _textBox.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(Int32.MaxValue, Int32.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(Int32.MaxValue, Int32.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(Int32.MaxValue, Int32.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; // _textBox.Location = new Point(Int32.MaxValue, Int32.MaxValue); _editControl = _readOnlyTextBoxLabel; } else { _readOnlyTextBoxLabel.Visible = false; // _readOnlyTextBoxLabel.Location = new Point(Int32.MaxValue, Int32.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 { return 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(SR.GetString(SR.DesignerActionPanel_ErrorActivatingDropDown, ex.Message)); } } else { ListBox listBox = new ListBox(); listBox.BorderStyle = BorderStyle.None; listBox.IntegralHeight = false; listBox.Font = ActionPanel.Font; listBox.SelectedIndexChanged += new EventHandler(OnListBoxSelectedIndexChanged); listBox.KeyDown += new KeyEventHandler(OnListBoxKeyDown); TypeConverter.StandardValuesCollection standardValues = GetStandardValues(); if (standardValues != null) { foreach (object o in standardValues) { string newItem = PropertyDescriptor.Converter.ConvertToString(TypeDescriptorContext, CultureInfo.CurrentCulture, o); listBox.Items.Add(newItem); if ((o != null) && o.Equals(Value)) { listBox.SelectedItem = newItem; } } } // All measurement code borrowed from WinForms PropertyGridView.cs int maxWidth = 0; // The listbox draws with GDI, not GDI+. So, we // use a normal DC here. IntPtr hdc = UnsafeNativeMethods.GetDC(new HandleRef(listBox, listBox.Handle)); IntPtr hFont = listBox.Font.ToHfont(); NativeMethods.CommonHandles.GdiHandleCollector.Add(); NativeMethods.TEXTMETRIC tm = new NativeMethods.TEXTMETRIC(); try { hFont = SafeNativeMethods.SelectObject(new HandleRef(listBox, hdc), new HandleRef(listBox.Font, hFont)); if (listBox.Items.Count > 0) { NativeMethods.SIZE textSize = new NativeMethods.SIZE(); foreach (string s in listBox.Items) { SafeNativeMethods.GetTextExtentPoint32(new HandleRef(listBox, hdc), s, s.Length, textSize); maxWidth = Math.Max((int)textSize.cx, maxWidth); } } SafeNativeMethods.GetTextMetrics(new HandleRef(listBox, hdc), ref tm); // border + padding + scrollbar maxWidth += 2 + tm.tmMaxCharWidth + SystemInformation.VerticalScrollBarWidth; hFont = SafeNativeMethods.SelectObject(new HandleRef(listBox, hdc), new HandleRef(listBox.Font, hFont)); } finally { SafeNativeMethods.DeleteObject(new HandleRef(listBox.Font, hFont)); UnsafeNativeMethods.ReleaseDC(new HandleRef(listBox, listBox.Handle), new HandleRef(listBox, hdc)); } listBox.Height = Math.Max(tm.tmHeight + 2, Math.Min(ListBoxMaximumHeight, listBox.PreferredHeight)); listBox.Width = Math.Max(maxWidth, EditRegionSize.Width); _ignoreDropDownValue = false; try { ShowDropDown(listBox, SystemColors.ControlDark); } finally { listBox.SelectedIndexChanged -= new EventHandler(OnListBoxSelectedIndexChanged); listBox.KeyDown -= new KeyEventHandler(OnListBoxKeyDown); } if (!_ignoreDropDownValue) { if (listBox.SelectedItem != null) { SetValue(listBox.SelectedItem); } } } } protected override void AddControls(Listcontrols) { 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) { bool isDropDown; if (_editor != null) { isDropDown = (_editor.GetEditStyle(TypeDescriptorContext) == UITypeEditorEditStyle.DropDown); } else { isDropDown = true; } 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 (!ActionPanel.DropDownActive) { if ((keyData == (Keys.Alt | Keys.Down)) || (keyData == (Keys.Alt | Keys.Up)) || (keyData == Keys.F4)) { ActivateDropDown(); 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 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 Control _hostedControl; private 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.FocusInternal(); _hostedControl.Focus(); } } // Lifted directly from PropertyGridView.DropDownHolder. Less destructive than using ShowDialog(). public void DoModalLoop() { while (Visible) { #if MVWASSEMBLY System.Windows.Forms.Application.DoEvents(); #else Application.DoEvents(); #endif UnsafeNativeMethods.MsgWaitForMultipleObjects(0, IntPtr.Zero, true, 250, NativeMethods.QS_ALLINPUT); } } /// /// 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), // NOTE: Forces handle creation 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((int)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; ToolStripDropDown dropDown = toplevel as ToolStripDropDown; if (dropDown != null) { // 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; internal static class Util { public static int LOWORD(int n) { return n & 0xffff; } } public static class CommonHandles { public static HandleCollector GdiHandleCollector = new HandleCollector("GDI", 500); public static HandleCollector HdcHandleCollector = new HandleCollector("HDC", 2); } [StructLayout(LayoutKind.Sequential)] public class SIZE { public int cx=0; public int cy=0; public SIZE() { } } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct TEXTMETRIC { public int tmHeight; public int tmAscent; public int tmDescent; public int tmInternalLeading; public int tmExternalLeading; public int tmAveCharWidth; public int tmMaxCharWidth; public int tmWeight; public int tmOverhang; public int tmDigitizedAspectX; public int tmDigitizedAspectY; public char tmFirstChar; public char tmLastChar; public char tmDefaultChar; public char tmBreakChar; public byte tmItalic; public byte tmUnderlined; public byte tmStruckOut; public byte tmPitchAndFamily; public byte tmCharSet; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct TEXTMETRICA { public int tmHeight; public int tmAscent; public int tmDescent; public int tmInternalLeading; public int tmExternalLeading; public int tmAveCharWidth; public int tmMaxCharWidth; public int tmWeight; public int tmOverhang; public int tmDigitizedAspectX; public int tmDigitizedAspectY; public byte tmFirstChar; public byte tmLastChar; public byte tmDefaultChar; public byte tmBreakChar; public byte tmItalic; public byte tmUnderlined; public byte tmStruckOut; public byte tmPitchAndFamily; public byte tmCharSet; } } private static class SafeNativeMethods { [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, EntryPoint = "DeleteObject", CharSet = System.Runtime.InteropServices.CharSet.Auto)] private static extern bool IntDeleteObject(HandleRef hObject); public static bool DeleteObject(HandleRef hObject) { NativeMethods.CommonHandles.GdiHandleCollector.Remove(); return IntDeleteObject(hObject); } [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] public static extern bool ReleaseCapture(); [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] public static extern IntPtr SelectObject(HandleRef hDC, HandleRef hObject); [DllImport(ExternDllGdi32, SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] public static extern int GetTextExtentPoint32(HandleRef hDC, string str, int len, [In, Out] NativeMethods.SIZE size); [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] public static extern int GetTextMetricsW(HandleRef hDC, [In, Out] ref NativeMethods.TEXTMETRIC lptm); [DllImport(ExternDllGdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi)] public static extern int GetTextMetricsA(HandleRef hDC, [In, Out] ref NativeMethods.TEXTMETRICA lptm); public static int GetTextMetrics(HandleRef hDC, ref NativeMethods.TEXTMETRIC lptm) { if (Marshal.SystemDefaultCharSize == 1) { // ANSI NativeMethods.TEXTMETRICA lptmA = new NativeMethods.TEXTMETRICA(); int retVal = SafeNativeMethods.GetTextMetricsA(hDC, ref lptmA); lptm.tmHeight = lptmA.tmHeight; lptm.tmAscent = lptmA.tmAscent; lptm.tmDescent = lptmA.tmDescent; lptm.tmInternalLeading = lptmA.tmInternalLeading; lptm.tmExternalLeading = lptmA.tmExternalLeading; lptm.tmAveCharWidth = lptmA.tmAveCharWidth; lptm.tmMaxCharWidth = lptmA.tmMaxCharWidth; lptm.tmWeight = lptmA.tmWeight; lptm.tmOverhang = lptmA.tmOverhang; lptm.tmDigitizedAspectX = lptmA.tmDigitizedAspectX; lptm.tmDigitizedAspectY = lptmA.tmDigitizedAspectY; lptm.tmFirstChar = (char)lptmA.tmFirstChar; lptm.tmLastChar = (char)lptmA.tmLastChar; lptm.tmDefaultChar = (char)lptmA.tmDefaultChar; lptm.tmBreakChar = (char)lptmA.tmBreakChar; lptm.tmItalic = lptmA.tmItalic; lptm.tmUnderlined = lptmA.tmUnderlined; lptm.tmStruckOut = lptmA.tmStruckOut; lptm.tmPitchAndFamily = lptmA.tmPitchAndFamily; lptm.tmCharSet = lptmA.tmCharSet; return retVal; } else { // Unicode return SafeNativeMethods.GetTextMetricsW(hDC, ref lptm); } } } private static class UnsafeNativeMethods { [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] public static extern IntPtr GetWindowLong(HandleRef hWnd, int nIndex); [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] public static extern IntPtr SetWindowLong(HandleRef hWnd, int nIndex, HandleRef dwNewLong); [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] public static extern int MsgWaitForMultipleObjects(int nCount, IntPtr pHandles, bool fWaitAll, int dwMilliseconds, int dwWakeMask); [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam); [DllImport(ExternDllUser32, CharSet = CharSet.Auto)] public static extern IntPtr GetCapture(); [DllImport(ExternDllUser32, ExactSpelling = true, EntryPoint = "GetDC", CharSet = CharSet.Auto)] private static extern IntPtr IntGetDC(HandleRef hWnd); public static IntPtr GetDC(HandleRef hWnd) { NativeMethods.CommonHandles.HdcHandleCollector.Add(); return IntGetDC(hWnd); } [DllImport(ExternDllUser32, ExactSpelling = true, EntryPoint = "ReleaseDC", CharSet = CharSet.Auto)] private static extern int IntReleaseDC(HandleRef hWnd, HandleRef hDC); public static int ReleaseDC(HandleRef hWnd, HandleRef hDC) { NativeMethods.CommonHandles.HdcHandleCollector.Remove(); return IntReleaseDC(hWnd, hDC); } } #endregion // Class that renders either the ellipsis or dropdown button internal sealed class EditorButton : Button { private bool _mouseOver; private bool _mouseDown; private bool _ellipsis; protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (e.Button == MouseButtons.Left) { _mouseDown = true; } } protected override void OnMouseEnter(EventArgs e) { base.OnMouseEnter(e); _mouseOver = true; } protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); _mouseOver = false; } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); if (e.Button == MouseButtons.Left) { _mouseDown = false; } } public bool Ellipsis { get { return _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(); cm.OldColor = Color.Black; cm.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(Listcontrols) { _label = new Label(); _label.BackColor = Color.Transparent; _label.TextAlign = ContentAlignment.MiddleLeft; _label.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(Int32.MaxValue, Int32.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.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 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 { return String.Empty; } } 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) { } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- MonitoringDescriptionAttribute.cs
- VisualProxy.cs
- UxThemeWrapper.cs
- XmlSchemaComplexContent.cs
- LineSegment.cs
- TreeNode.cs
- AppDomainEvidenceFactory.cs
- MailWriter.cs
- WinInetCache.cs
- UseManagedPresentationBindingElement.cs
- WindowsTitleBar.cs
- DataColumnPropertyDescriptor.cs
- StaticFileHandler.cs
- ClientConfigurationSystem.cs
- PropertyRef.cs
- TiffBitmapDecoder.cs
- SqlWorkflowPersistenceService.cs
- UserUseLicenseDictionaryLoader.cs
- TabItemWrapperAutomationPeer.cs
- ZipFileInfoCollection.cs
- codemethodreferenceexpression.cs
- Avt.cs
- glyphs.cs
- ObjectConverter.cs
- HttpValueCollection.cs
- ConstructorArgumentAttribute.cs
- SafeCoTaskMem.cs
- OleDbRowUpdatingEvent.cs
- Transform.cs
- RedistVersionInfo.cs
- CodeMethodInvokeExpression.cs
- QilInvokeEarlyBound.cs
- ReadOnlyDictionary.cs
- SecuritySessionSecurityTokenAuthenticator.cs
- Canvas.cs
- Timer.cs
- GPRECT.cs
- ConfigurationPropertyCollection.cs
- DefaultValueMapping.cs
- SignedPkcs7.cs
- ImportContext.cs
- ValidationEventArgs.cs
- TemplatePropertyEntry.cs
- ColumnHeaderConverter.cs
- GregorianCalendar.cs
- SmiGettersStream.cs
- DataKeyArray.cs
- SecurityKeyUsage.cs
- SystemThemeKey.cs
- CollectionViewProxy.cs
- DbDeleteCommandTree.cs
- IdleTimeoutMonitor.cs
- SelectorAutomationPeer.cs
- WindowClosedEventArgs.cs
- Unit.cs
- HostingPreferredMapPath.cs
- EventHandlerService.cs
- EmptyStringExpandableObjectConverter.cs
- MatrixTransform.cs
- LocationUpdates.cs
- ObjectToIdCache.cs
- DirectionalLight.cs
- SelectedGridItemChangedEvent.cs
- ColumnClickEvent.cs
- ProfileProvider.cs
- EndPoint.cs
- SliderAutomationPeer.cs
- StandardCommandToolStripMenuItem.cs
- BufferedGraphicsManager.cs
- KoreanCalendar.cs
- BuilderInfo.cs
- StringDictionaryEditor.cs
- OperationGenerator.cs
- ObjectDisposedException.cs
- TableAutomationPeer.cs
- InkCanvasInnerCanvas.cs
- GC.cs
- ConditionCollection.cs
- DrawListViewSubItemEventArgs.cs
- BamlRecords.cs
- AuthenticationConfig.cs
- CurrentChangedEventManager.cs
- ServiceOperationInfoTypeConverter.cs
- EntityDataSourceWizardForm.cs
- VisualStateGroup.cs
- TextEndOfParagraph.cs
- CustomCredentialPolicy.cs
- SocketException.cs
- RadioButton.cs
- StreamGeometryContext.cs
- TcpChannelListener.cs
- KoreanCalendar.cs
- ResourceCodeDomSerializer.cs
- HTTPNotFoundHandler.cs
- LogicalChannel.cs
- XamlPathDataSerializer.cs
- ToolStripStatusLabel.cs
- XmlNodeReader.cs
- StringInfo.cs
- WebConfigurationManager.cs