Code:
/ DotNET / DotNET / 8.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);
}
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) {
//
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) {
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) {
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
List newLines = 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(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) {
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(List controls) {
_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
- EngineSite.cs
- StringTraceRecord.cs
- StaticExtension.cs
- AssociatedControlConverter.cs
- DynamicDiscoSearcher.cs
- sortedlist.cs
- HtmlInputPassword.cs
- OLEDB_Enum.cs
- SqlConnectionHelper.cs
- PrinterResolution.cs
- UnsafeNativeMethods.cs
- AliasGenerator.cs
- WorkflowQueue.cs
- TypeBuilderInstantiation.cs
- ActivitySurrogate.cs
- ArgumentDirectionHelper.cs
- TimeSpanSecondsOrInfiniteConverter.cs
- TrackingExtract.cs
- RSAProtectedConfigurationProvider.cs
- RelatedView.cs
- ConnectionStringSettings.cs
- SmiTypedGetterSetter.cs
- ViewDesigner.cs
- CFGGrammar.cs
- RefreshPropertiesAttribute.cs
- FileClassifier.cs
- XmlSchemaIdentityConstraint.cs
- TextChangedEventArgs.cs
- SchemaImporterExtension.cs
- AdornerLayer.cs
- ObjectParameterCollection.cs
- hresults.cs
- PageFunction.cs
- EntitySet.cs
- UnauthorizedAccessException.cs
- ArraySegment.cs
- ValueCollectionParameterReader.cs
- DataError.cs
- EventSinkHelperWriter.cs
- ArgIterator.cs
- SqlMethodAttribute.cs
- XPathConvert.cs
- Size3DConverter.cs
- WebPartConnectionsConfigureVerb.cs
- oledbmetadatacolumnnames.cs
- ScriptReferenceBase.cs
- COM2IDispatchConverter.cs
- MsmqChannelFactory.cs
- figurelengthconverter.cs
- VBIdentifierName.cs
- FocusWithinProperty.cs
- DataObjectCopyingEventArgs.cs
- XmlPropertyBag.cs
- DataTableMapping.cs
- ObjectConverter.cs
- UIElementParagraph.cs
- PageSetupDialog.cs
- SplineKeyFrames.cs
- InputManager.cs
- DataTableReader.cs
- CounterSet.cs
- CodeGenHelper.cs
- MenuItemBinding.cs
- PreviewKeyDownEventArgs.cs
- IApplicationTrustManager.cs
- NativeCompoundFileAPIs.cs
- ReferencedAssembly.cs
- SqlClientPermission.cs
- Point3DValueSerializer.cs
- ElementsClipboardData.cs
- RecordManager.cs
- ProxyWebPartManager.cs
- HierarchicalDataBoundControlAdapter.cs
- GenericRootAutomationPeer.cs
- PeerInvitationResponse.cs
- InvalidDataException.cs
- DoubleAnimationClockResource.cs
- IERequestCache.cs
- InputMethodStateTypeInfo.cs
- SafeFileMappingHandle.cs
- DebugView.cs
- SettingsPropertyCollection.cs
- BorderGapMaskConverter.cs
- SignedXml.cs
- JoinQueryOperator.cs
- ContainerAction.cs
- HttpWriter.cs
- MobileUserControlDesigner.cs
- LinqDataSourceEditData.cs
- SendKeys.cs
- Utils.cs
- Size.cs
- DragAssistanceManager.cs
- WebUtil.cs
- ping.cs
- TableRow.cs
- Form.cs
- XmlWellformedWriterHelpers.cs
- ResourceAttributes.cs
- DataGridRow.cs