Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / Designer / CompMod / System / ComponentModel / Design / CollectionEditor.cs / 1 / CollectionEditor.cs
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------------
/*
*/
namespace System.ComponentModel.Design {
using System.Design;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Remoting.Activation;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System;
using System.Collections;
using Microsoft.Win32;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Drawing.Design;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Forms.ComponentModel;
using System.Windows.Forms.Design;
using System.Windows.Forms.VisualStyles;
using System.Runtime.Serialization.Formatters.Binary;
using System.Globalization;
///
///
/// Provides a generic editor for most any collection.
///
public class CollectionEditor : UITypeEditor {
private Type type;
private Type collectionItemType;
private Type[] newItemTypes;
private ITypeDescriptorContext currentContext;
private bool ignoreChangedEvents = false;
private bool ignoreChangingEvents = false;
///
///
///
/// Useful for derived classes to do processing when cancelling changes
///
///
protected virtual void CancelChanges() {
}
///
///
///
/// Initializes a new instance of the class using the
/// specified collection type.
///
///
public CollectionEditor(Type type) {
this.type = type;
}
///
///
/// Gets or sets the data type of each item in the collection.
///
protected Type CollectionItemType {
get {
if (collectionItemType == null) {
collectionItemType = CreateCollectionItemType();
}
return collectionItemType;
}
}
///
///
///
/// Gets or sets the type of the collection.
///
///
protected Type CollectionType {
get {
return type;
}
}
///
///
///
/// Gets or sets a type descriptor that indicates the current context.
///
///
protected ITypeDescriptorContext Context {
get {
return currentContext;
}
}
///
///
/// Gets or sets
/// the available item types that can be created for this collection.
///
protected Type[] NewItemTypes {
get {
if (newItemTypes == null) {
newItemTypes = CreateNewItemTypes();
}
return newItemTypes;
}
}
///
///
/// Gets the help topic to display for the dialog help button or pressing F1. Override to
/// display a different help topic.
///
protected virtual string HelpTopic {
get {
return "net.ComponentModel.CollectionEditor";
}
}
///
///
/// Gets or sets a value indicating whether original members of the collection can be removed.
///
protected virtual bool CanRemoveInstance(object value) {
IComponent comp = value as IComponent;
if (comp != null) {
// Make sure the component is not being inherited -- we can't delete these!
//
InheritanceAttribute ia = (InheritanceAttribute)TypeDescriptor.GetAttributes(comp)[typeof(InheritanceAttribute)];
if (ia != null && ia.InheritanceLevel != InheritanceLevel.NotInherited) {
return false;
}
}
return true;
}
///
///
/// Gets or sets a value indicating whether multiple collection members can be
/// selected.
///
protected virtual bool CanSelectMultipleInstances() {
return true;
}
///
///
/// Creates a
/// new form to show the current collection.
///
protected virtual CollectionForm CreateCollectionForm() {
return new CollectionEditorCollectionForm(this);
}
///
///
///
/// Creates a new instance of the specified collection item type.
///
///
protected virtual object CreateInstance(Type itemType) {
return CollectionEditor.CreateInstance(itemType, (IDesignerHost)GetService(typeof(IDesignerHost)), null);
}
///
///
///
/// This Function gets the object from the givem object. The input is an arrayList returned as an Object.
/// The output is a arraylist which contains the individual objects that need to be created.
///
///
protected virtual IList GetObjectsFromInstance(object instance) {
ArrayList ret = new ArrayList();
ret.Add(instance);
return ret;
}
internal static object CreateInstance(Type itemType, IDesignerHost host, string name) {
object instance = null;
if (typeof(IComponent).IsAssignableFrom(itemType)) {
if (host != null) {
instance = host.CreateComponent(itemType, (string)name);
// Set component defaults
if (host != null) {
IComponentInitializer init = host.GetDesigner((IComponent)instance) as IComponentInitializer;
if (init != null) {
init.InitializeNewComponent(null);
}
}
}
}
if (instance == null) {
instance = TypeDescriptor.CreateInstance(host, itemType, null, null);
}
return instance;
}
///
///
/// Retrieves the display text for the given list item.
///
protected virtual string GetDisplayText(object value) {
string text;
if (value == null) {
return string.Empty;
}
PropertyDescriptor prop = TypeDescriptor.GetProperties(value)["Name"];
if (prop != null && prop.PropertyType == typeof(string)) {
text = (string) prop.GetValue( value );
if (text != null && text.Length > 0) {
return text;
}
}
prop = TypeDescriptor.GetDefaultProperty(CollectionType);
if (prop != null && prop.PropertyType == typeof(string)) {
text = (string)prop.GetValue(value);
if (text != null && text.Length > 0) {
return text;
}
}
text = TypeDescriptor.GetConverter(value).ConvertToString(value);
if (text == null || text.Length == 0) {
text = value.GetType().Name;
}
return text;
}
///
///
///
/// Gets an instance of
/// the data type this collection contains.
///
///
protected virtual Type CreateCollectionItemType() {
PropertyInfo[] props = TypeDescriptor.GetReflectionType(CollectionType).GetProperties(BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < props.Length; i++) {
if (props[i].Name.Equals("Item") || props[i].Name.Equals("Items")) {
return props[i].PropertyType;
}
}
// Couldn't find anything. Return Object
Debug.Fail("Collection " + CollectionType.FullName + " contains no Item or Items property so we cannot display and edit any values");
return typeof(object);
}
///
///
///
/// Gets the data
/// types this collection editor can create.
///
///
protected virtual Type[] CreateNewItemTypes() {
return new Type[] {CollectionItemType};
}
///
///
///
/// Destroys the specified instance of the object.
///
///
protected virtual void DestroyInstance(object instance) {
IComponent compInstance = instance as IComponent;
if (compInstance != null) {
IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost));
if (host != null) {
host.DestroyComponent(compInstance);
}
else {
compInstance.Dispose();
}
}
else {
IDisposable dispInstance = instance as IDisposable;
if (dispInstance != null) {
dispInstance.Dispose();
}
}
}
///
///
/// Edits the specified object value using the editor style
/// provided by .
///
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] // everything in this assembly is full trust.
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
if (provider != null) {
IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
if (edSvc != null) {
this.currentContext = context;
// Always create a new CollectionForm. We used to do reuse the form in V1 and Everett
// but this implies that the form will never be disposed.
CollectionForm localCollectionForm = CreateCollectionForm();
ITypeDescriptorContext lastContext = currentContext;
localCollectionForm.EditValue = value;
ignoreChangingEvents = false;
ignoreChangedEvents = false;
DesignerTransaction trans = null;
bool commitChange = true;
IComponentChangeService cs = null;
IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost));
try {
try {
if (host != null) {
trans = host.CreateTransaction(SR.GetString(SR.CollectionEditorUndoBatchDesc, CollectionItemType.Name));
}
}
catch(CheckoutException cxe) {
if (cxe == CheckoutException.Canceled)
return value;
throw cxe;
}
cs = host != null ? (IComponentChangeService)host.GetService(typeof(IComponentChangeService)) : null;
if (cs != null) {
cs.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged);
cs.ComponentChanging += new ComponentChangingEventHandler(this.OnComponentChanging);
}
if (localCollectionForm.ShowEditorDialog(edSvc) == DialogResult.OK) {
value = localCollectionForm.EditValue;
}
else {
commitChange = false;
}
}
finally {
localCollectionForm.EditValue = null;
this.currentContext = lastContext;
if (trans != null) {
if (commitChange) {
trans.Commit();
}
else {
trans.Cancel();
}
}
if (cs != null) {
cs.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
cs.ComponentChanging -= new ComponentChangingEventHandler(this.OnComponentChanging);
}
localCollectionForm.Dispose();
}
}
}
return value;
}
///
///
/// Gets the editing style of the Edit method.
///
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] // everything in this assembly is full trust.
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) {
return UITypeEditorEditStyle.Modal;
}
private bool IsAnyObjectInheritedReadOnly(object[] items) {
// If the object implements IComponent, and is not sited, check with
// the inheritance service (if it exists) to see if this is a component
// that is being inherited from another class. If it is, then we do
// not want to place it in the collection editor. If the inheritance service
// chose not to site the component, that indicates it should be hidden from
// the user.
IInheritanceService iSvc = null;
bool checkISvc = false;
foreach(object o in items) {
IComponent comp = o as IComponent;
if (comp != null && comp.Site == null) {
if (!checkISvc) {
checkISvc = true;
if (Context != null) {
iSvc = (IInheritanceService)Context.GetService(typeof(IInheritanceService));
}
}
if (iSvc != null && iSvc.GetInheritanceAttribute(comp).Equals(InheritanceAttribute.InheritedReadOnly)) {
return true;
}
}
}
return false;
}
///
///
/// Converts the specified collection into an array of objects.
///
protected virtual object[] GetItems(object editValue) {
if (editValue != null) {
// We look to see if the value implements ICollection, and if it does,
// we set through that.
//
if (editValue is System.Collections.ICollection) {
ArrayList list = new ArrayList();
System.Collections.ICollection col = (System.Collections.ICollection)editValue;
foreach(object o in col) {
list.Add(o);
}
object[] values = new object[list.Count];
list.CopyTo(values, 0);
return values;
}
}
return new object[0];
}
///
///
///
/// Gets the requested service, if it is available.
///
///
protected object GetService(Type serviceType) {
if (Context != null) {
return Context.GetService(serviceType);
}
return null;
}
///
///
/// reflect any change events to the instance object
///
private void OnComponentChanged(object sender, ComponentChangedEventArgs e) {
if (!ignoreChangedEvents && sender != Context.Instance) {
ignoreChangedEvents = true;
Context.OnComponentChanged();
}
}
///
///
/// reflect any changed events to the instance object
///
private void OnComponentChanging(object sender, ComponentChangingEventArgs e) {
if (!ignoreChangingEvents && sender != Context.Instance) {
ignoreChangingEvents = true;
Context.OnComponentChanging();
}
}
///
///
///
/// Removes the item from the column header from the listview column header collection
///
///
internal virtual void OnItemRemoving(object item) {
}
///
///
///
/// Sets
/// the specified collection to have the specified array of items.
///
///
protected virtual object SetItems(object editValue, object[] value) {
if (editValue != null) {
Array oldValue = (Array)GetItems(editValue);
bool valueSame = (oldValue.Length == value.Length);
// We look to see if the value implements IList, and if it does,
// we set through that.
//
Debug.Assert(editValue is System.Collections.IList, "editValue is not an IList");
if (editValue is System.Collections.IList) {
System.Collections.IList list = (System.Collections.IList)editValue;
list.Clear();
for (int i = 0; i < value.Length; i++) {
list.Add(value[i]);
}
}
}
return editValue;
}
///
///
///
/// Called when the help button is clicked.
///
///
protected virtual void ShowHelp() {
IHelpService helpService = GetService(typeof(IHelpService)) as IHelpService;
if (helpService != null) {
helpService.ShowHelpFromKeyword(HelpTopic);
}
else {
Debug.Fail("Unable to get IHelpService.");
}
}
internal class SplitButton : Button
{
private PushButtonState _state;
private const int pushButtonWidth = 14;
private Rectangle dropDownRectangle = new Rectangle();
private bool showSplit = false;
public bool ShowSplit
{
set
{
if (value != showSplit)
{
showSplit = value;
Invalidate();
}
}
}
private PushButtonState State
{
get
{
return _state;
}
set
{
if (!_state.Equals(value))
{
_state = value;
Invalidate();
}
}
}
public override Size GetPreferredSize(Size proposedSize)
{
Size preferredSize = base.GetPreferredSize(proposedSize);
if (showSplit && !string.IsNullOrEmpty(Text) && TextRenderer.MeasureText(Text, Font).Width + pushButtonWidth > preferredSize.Width)
{
return preferredSize + new Size(pushButtonWidth, 0);
}
return preferredSize;
}
protected override bool IsInputKey(Keys keyData)
{
if (keyData.Equals(Keys.Down) && showSplit)
{
return true;
}
else
{
return base.IsInputKey(keyData);
}
}
protected override void OnGotFocus(EventArgs e)
{
if (!showSplit)
{
base.OnGotFocus(e);
return;
}
if (!State.Equals(PushButtonState.Pressed) && !State.Equals(PushButtonState.Disabled))
{
State = PushButtonState.Default;
}
}
protected override void OnKeyDown(KeyEventArgs kevent)
{
if (kevent.KeyCode.Equals(Keys.Down) && showSplit)
{
ShowContextMenuStrip();
}
}
protected override void OnLostFocus(EventArgs e)
{
if (!showSplit)
{
base.OnLostFocus(e);
return;
}
if (!State.Equals(PushButtonState.Pressed) && !State.Equals(PushButtonState.Disabled))
{
State = PushButtonState.Normal;
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (!showSplit)
{
base.OnMouseDown(e);
return;
}
if (dropDownRectangle.Contains(e.Location))
{
ShowContextMenuStrip();
}
else
{
State = PushButtonState.Pressed;
}
}
protected override void OnMouseEnter(EventArgs e)
{
if (!showSplit)
{
base.OnMouseEnter(e);
return;
}
if (!State.Equals(PushButtonState.Pressed) && !State.Equals(PushButtonState.Disabled))
{
State = PushButtonState.Hot;
}
}
protected override void OnMouseLeave(EventArgs e)
{
if (!showSplit)
{
base.OnMouseLeave(e);
return;
}
if (!State.Equals(PushButtonState.Pressed) && !State.Equals(PushButtonState.Disabled))
{
if (Focused)
{
State = PushButtonState.Default;
}
else
{
State = PushButtonState.Normal;
}
}
}
protected override void OnMouseUp(MouseEventArgs mevent)
{
if (!showSplit)
{
base.OnMouseUp(mevent);
return;
}
if (ContextMenuStrip == null || !ContextMenuStrip.Visible)
{
SetButtonDrawState();
if (Bounds.Contains(Parent.PointToClient(Cursor.Position)) && !dropDownRectangle.Contains(mevent.Location))
{
OnClick(new EventArgs());
}
}
}
protected override void OnPaint(PaintEventArgs pevent)
{
base.OnPaint(pevent);
if (!showSplit)
{
return;
}
Graphics g = pevent.Graphics;
Rectangle bounds = new Rectangle(0, 0, Width, Height);
TextFormatFlags formatFlags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter;
ButtonRenderer.DrawButton(g, bounds, State);
dropDownRectangle = new Rectangle(bounds.Right - pushButtonWidth - 1, 4, pushButtonWidth, bounds.Height - 8);
if (RightToLeft == RightToLeft.Yes) {
dropDownRectangle.X = bounds.Left + 1;
g.DrawLine(SystemPens.ButtonHighlight, bounds.Left + pushButtonWidth, 4, bounds.Left + pushButtonWidth, bounds.Bottom -4);
g.DrawLine(SystemPens.ButtonHighlight, bounds.Left + pushButtonWidth + 1, 4, bounds.Left + pushButtonWidth + 1, bounds.Bottom -4);
bounds.Offset(pushButtonWidth, 0);
bounds.Width = bounds.Width - pushButtonWidth;
}
else {
g.DrawLine(SystemPens.ButtonHighlight, bounds.Right - pushButtonWidth, 4, bounds.Right - pushButtonWidth, bounds.Bottom -4);
g.DrawLine(SystemPens.ButtonHighlight, bounds.Right - pushButtonWidth - 1, 4, bounds.Right - pushButtonWidth - 1, bounds.Bottom -4);
bounds.Width = bounds.Width - pushButtonWidth;
}
PaintArrow(g, dropDownRectangle);
// If we dont' use mnemonic, set formatFlag to NoPrefix as this will show ampersand.
if (!UseMnemonic) {
formatFlags = formatFlags | TextFormatFlags.NoPrefix;
}
else if (!ShowKeyboardCues) {
formatFlags = formatFlags | TextFormatFlags.HidePrefix;
}
if (!string.IsNullOrEmpty(this.Text)) {
TextRenderer.DrawText(g, Text, Font, bounds, SystemColors.ControlText, formatFlags);
}
if (Focused) {
bounds.Inflate(-4,-4);
//ControlPaint.DrawFocusRectangle(g, bounds);
}
}
private void PaintArrow(Graphics g, Rectangle dropDownRect) {
Point middle = new Point(Convert.ToInt32(dropDownRect.Left + dropDownRect.Width / 2), Convert.ToInt32(dropDownRect.Top + dropDownRect.Height / 2));
//if the width is odd - favor pushing it over one pixel right.
middle.X += (dropDownRect.Width % 2);
Point[] arrow = new Point[] {new Point(middle.X - 2, middle.Y - 1), new Point(middle.X + 3, middle.Y - 1), new Point(middle.X, middle.Y + 2)};
g.FillPolygon(SystemBrushes.ControlText, arrow);
}
private void ShowContextMenuStrip() {
State = PushButtonState.Pressed;
if (ContextMenuStrip != null) {
ContextMenuStrip.Closed += new ToolStripDropDownClosedEventHandler(ContextMenuStrip_Closed);
ContextMenuStrip.Show(this, 0, Height);
}
}
private void ContextMenuStrip_Closed(object sender, ToolStripDropDownClosedEventArgs e)
{
ContextMenuStrip cms = sender as ContextMenuStrip;
if (cms != null)
{
cms.Closed -= new ToolStripDropDownClosedEventHandler(ContextMenuStrip_Closed);
}
SetButtonDrawState();
}
private void SetButtonDrawState()
{
if (Bounds.Contains(Parent.PointToClient(Cursor.Position)))
{
State = PushButtonState.Hot;
}
else if (Focused)
{
State = PushButtonState.Default;
}
else
{
State = PushButtonState.Normal;
}
}
}
///
///
/// This is the collection editor's default implementation of a
/// collection form.
///
private class CollectionEditorCollectionForm : CollectionForm {
private const int TEXT_INDENT = 1;
private const int PAINT_WIDTH = 20;
private const int PAINT_INDENT = 26;
private static readonly double LOG10 = Math.Log(10);
// Manipulation of the collection.
//
private ArrayList createdItems;
private ArrayList removedItems;
private ArrayList originalItems;
// Calling Editor
private CollectionEditor editor;
// Dialog UI
//
private FilterListBox listbox;
private SplitButton addButton;
private Button removeButton;
private Button cancelButton;
private Button okButton;
private Button downButton;
private Button upButton;
private VsPropertyGrid propertyBrowser;
private Label membersLabel;
private Label propertiesLabel;
private ContextMenuStrip addDownMenu;
private TableLayoutPanel okCancelTableLayoutPanel;
private TableLayoutPanel overArchingTableLayoutPanel;
private TableLayoutPanel addRemoveTableLayoutPanel;
// Prevent flicker when switching selection
private int suspendEnabledCount = 0;
// our flag for if something changed
//
private bool dirty;
public CollectionEditorCollectionForm(CollectionEditor editor) : base(editor) {
this.editor = editor;
InitializeComponent();
this.Text = SR.GetString(SR.CollectionEditorCaption, CollectionItemType.Name);
HookEvents();
Type[] newItemTypes = NewItemTypes;
if (newItemTypes.Length > 1) {
EventHandler addDownMenuClick = new EventHandler(this.AddDownMenu_click);
addButton.ShowSplit = true;
addDownMenu = new ContextMenuStrip();
addButton.ContextMenuStrip = addDownMenu;
for (int i = 0; i < newItemTypes.Length; i++) {
addDownMenu.Items.Add(new TypeMenuItem(newItemTypes[i], addDownMenuClick));
}
}
AdjustListBoxItemHeight();
}
private bool IsImmutable {
get {
bool immutable = true;
// We are considered immutable if the converter is defined as requiring a
// create instance or all the properties are read-only.
//
if (!TypeDescriptor.GetConverter(CollectionItemType).GetCreateInstanceSupported()) {
foreach (PropertyDescriptor p in TypeDescriptor.GetProperties(CollectionItemType)) {
if (!p.IsReadOnly) {
immutable = false;
break;
}
}
}
return immutable;
}
}
///
///
/// Adds a new element to the collection.
///
private void AddButton_click(object sender, EventArgs e) {
PerformAdd();
}
///
///
/// Processes a click of the drop down type menu. This creates a
/// new instance.
///
private void AddDownMenu_click(object sender, EventArgs e) {
if (sender is TypeMenuItem) {
TypeMenuItem typeMenuItem = (TypeMenuItem) sender;
CreateAndAddInstance(typeMenuItem.ItemType);
}
}
///
///
///
/// This Function adds the individual objects to the ListBox.
///
private void AddItems(IList instances) {
if (createdItems == null) {
createdItems = new ArrayList();
}
listbox.BeginUpdate();
try {
foreach( object instance in instances ){
if (instance != null) {
dirty = true;
createdItems.Add(instance);
ListItem created = new ListItem(editor, instance);
listbox.Items.Add(created);
}
}
}
finally {
listbox.EndUpdate();
}
if (instances.Count == 1) {
// optimize for the case where we just added one thing...
UpdateItemWidths(listbox.Items[listbox.Items.Count -1] as ListItem);
}
else {
// othewise go through the entire list
UpdateItemWidths(null);
}
// Select the last item
//
SuspendEnabledUpdates();
try {
listbox.ClearSelected();
listbox.SelectedIndex = listbox.Items.Count - 1;
object[] items = new object[listbox.Items.Count];
for (int i = 0; i < items.Length; i++) {
items[i] = ((ListItem)listbox.Items[i]).Value;
}
Items = items;
//fringe case -- someone changes the edit value which resets the selindex, we should keep the new index.
if (listbox.Items.Count > 0 && listbox.SelectedIndex != listbox.Items.Count - 1) {
listbox.ClearSelected();
listbox.SelectedIndex = listbox.Items.Count - 1;
}
}
finally {
ResumeEnabledUpdates(true);
}
}
private void AdjustListBoxItemHeight() {
listbox.ItemHeight = Font.Height + SystemInformation.BorderSize.Width*2;
}
///
/// Determines whether removal of a specific list item should be permitted.
/// Used to determine enabled/disabled state of the Remove (X) button.
/// Items added after editor was opened may always be removed.
/// Items that existed before editor was opened require a call to CanRemoveInstance.
///
private bool AllowRemoveInstance(object value) {
if (createdItems != null && createdItems.Contains(value)) {
return true;
}
else {
return CanRemoveInstance(value);
}
}
private int CalcItemWidth(Graphics g, ListItem item) {
int c = listbox.Items.Count;
if (c < 2) {
c = 2; //for c-1 should be greater than zero.
}
SizeF sizeW = g.MeasureString(c.ToString(CultureInfo.CurrentCulture), listbox.Font);
int charactersInNumber = ((int)(Math.Log((double)(c-1)) / LOG10) + 1);
int w = 4 + charactersInNumber * (Font.Height /2);
w = Math.Max(w, (int)Math.Ceiling(sizeW.Width));
w += SystemInformation.BorderSize.Width * 4;
SizeF size = g.MeasureString(GetDisplayText(item), listbox.Font);
int pic = 0;
if (item.Editor != null && item.Editor.GetPaintValueSupported()) {
pic = PAINT_WIDTH + TEXT_INDENT;
}
return (int)Math.Ceiling(size.Width) + w + pic + SystemInformation.BorderSize.Width * 4;
}
///
///
/// Aborts changes made in the editor.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
private void CancelButton_click(object sender, EventArgs e) {
try {
editor.CancelChanges();
if (!CollectionEditable || !dirty) {
return;
}
dirty = false;
listbox.Items.Clear();
if (createdItems != null) {
object[] items = createdItems.ToArray();
if(items.Length > 0 && items[0] is IComponent && ((IComponent)items[0]).Site != null) {
// here we bail now because we don't want to do the "undo" manually,
// we're part of a trasaction, we've added item, the rollback will be
// handled by the undo engine because the component in the collection are sited
// doing it here kills perfs because the undo of the transaction has to rollback the remove and then
// rollback the add. This is useless and is only needed for non sited component or other classes
return;
}
for (int i=0; i 0)) {
object[] items = new object[originalItems.Count];
for (int i = 0; i < originalItems.Count; i++) {
items[i] = originalItems[i];
}
Items = items;
originalItems.Clear();
}
else {
Items = new object[0];
}
}
catch (Exception ex) {
DialogResult = DialogResult.None;
DisplayError(ex);
}
}
///
///
/// Performs a create instance and then adds the instance to
/// the list box.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
private void CreateAndAddInstance(Type type) {
try {
object instance = CreateInstance(type);
IList multipleInstance = editor.GetObjectsFromInstance(instance);
if (multipleInstance != null) {
AddItems(multipleInstance);
}
}
catch (Exception e) {
DisplayError(e);
}
}
///
///
/// Moves the selected item down one.
///
private void DownButton_click(object sender, EventArgs e) {
try {
SuspendEnabledUpdates();
dirty = true;
int index = listbox.SelectedIndex;
if (index == listbox.Items.Count - 1)
return;
int ti = listbox.TopIndex;
object itemMove = listbox.Items[index];
listbox.Items[index] = listbox.Items[index+1];
listbox.Items[index+1] = itemMove;
if (ti < listbox.Items.Count - 1)
listbox.TopIndex = ti + 1;
listbox.ClearSelected();
listbox.SelectedIndex = index + 1;
// enabling/disabling the buttons has moved the focus to the OK button, move it back to the sender
Control ctrlSender = (Control)sender;
if (ctrlSender.Enabled) {
ctrlSender.Focus ();
}
}
finally {
ResumeEnabledUpdates(true);
}
}
private void CollectionEditor_HelpButtonClicked(object sender, CancelEventArgs e) {
e.Cancel = true;
editor.ShowHelp();
}
private void Form_HelpRequested(object sender, HelpEventArgs e) {
editor.ShowHelp();
}
///
///
/// Retrieves the display text for the given list item (if any). The item determines its own display text
/// through its ToString() method, which delegates to the GetDisplayText() override on the parent CollectionEditor.
/// This means in theory that the text can change at any time (ie. its not fixed when the item is added to the list).
/// The item returns its display text through ToString() so that the same text will be reported to Accessibility clients.
///
private string GetDisplayText(ListItem item) {
return (item == null) ? String.Empty : item.ToString();
}
private void HookEvents() {
listbox.KeyDown += new KeyEventHandler(this.Listbox_keyDown);
listbox.DrawItem += new DrawItemEventHandler(this.Listbox_drawItem);
listbox.SelectedIndexChanged += new EventHandler(this.Listbox_selectedIndexChanged);
listbox.HandleCreated += new EventHandler(this.Listbox_handleCreated);
upButton.Click += new EventHandler(this.UpButton_click);
downButton.Click += new EventHandler(this.DownButton_click);
propertyBrowser.PropertyValueChanged += new PropertyValueChangedEventHandler(this.PropertyGrid_propertyValueChanged);
addButton.Click += new EventHandler(this.AddButton_click);
removeButton.Click += new EventHandler(this.RemoveButton_click);
okButton.Click += new EventHandler(this.OKButton_click);
cancelButton.Click += new EventHandler(this.CancelButton_click);
this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.CollectionEditor_HelpButtonClicked);
this.HelpRequested += new HelpEventHandler(this.Form_HelpRequested);
}
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CollectionEditor));
this.membersLabel = new System.Windows.Forms.Label();
this.listbox = new FilterListBox();
this.upButton = new Button();
this.downButton = new Button();
this.propertiesLabel = new System.Windows.Forms.Label();
this.propertyBrowser = new VsPropertyGrid(Context);
this.addButton = new SplitButton();
this.removeButton = new System.Windows.Forms.Button();
this.okButton = new System.Windows.Forms.Button();
this.cancelButton = new System.Windows.Forms.Button();
this.okCancelTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
this.overArchingTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
this.addRemoveTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
this.okCancelTableLayoutPanel.SuspendLayout();
this.overArchingTableLayoutPanel.SuspendLayout();
this.addRemoveTableLayoutPanel.SuspendLayout();
this.SuspendLayout();
//
// membersLabel
//
resources.ApplyResources(this.membersLabel, "membersLabel");
this.membersLabel.Name = "membersLabel";
//
// listbox
//
resources.ApplyResources(this.listbox, "listbox");
this.listbox.SelectionMode = (CanSelectMultipleInstances() ? SelectionMode.MultiExtended : SelectionMode.One);
this.listbox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
this.listbox.FormattingEnabled = true;
this.listbox.Name = "listbox";
this.overArchingTableLayoutPanel.SetRowSpan(this.listbox, 2);
//
// upButton
//
resources.ApplyResources(this.upButton, "upButton");
this.upButton.Name = "upButton";
//
// downButton
//
resources.ApplyResources(this.downButton, "downButton");
this.downButton.Name = "downButton";
//
// propertiesLabel
//
resources.ApplyResources(this.propertiesLabel, "propertiesLabel");
this.propertiesLabel.AutoEllipsis = true;
this.propertiesLabel.Name = "propertiesLabel";
//
// propertyBrowser
//
resources.ApplyResources(this.propertyBrowser, "propertyBrowser");
this.propertyBrowser.CommandsVisibleIfAvailable = false;
this.propertyBrowser.Name = "propertyBrowser";
this.overArchingTableLayoutPanel.SetRowSpan(this.propertyBrowser, 3);
//
// addButton
//
resources.ApplyResources(this.addButton, "addButton");
this.addButton.Name = "addButton";
//
// removeButton
//
resources.ApplyResources(this.removeButton, "removeButton");
this.removeButton.Name = "removeButton";
//
// okButton
//
resources.ApplyResources(this.okButton, "okButton");
this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.okButton.Name = "okButton";
//
// cancelButton
//
resources.ApplyResources(this.cancelButton, "cancelButton");
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Name = "cancelButton";
//
// okCancelTableLayoutPanel
//
resources.ApplyResources(this.okCancelTableLayoutPanel, "okCancelTableLayoutPanel");
this.overArchingTableLayoutPanel.SetColumnSpan(this.okCancelTableLayoutPanel, 3);
this.okCancelTableLayoutPanel.Controls.Add(this.okButton, 0, 0);
this.okCancelTableLayoutPanel.Controls.Add(this.cancelButton, 1, 0);
this.okCancelTableLayoutPanel.Name = "okCancelTableLayoutPanel";
//
// overArchingTableLayoutPanel
//
resources.ApplyResources(this.overArchingTableLayoutPanel, "overArchingTableLayoutPanel");
this.overArchingTableLayoutPanel.Controls.Add(this.downButton, 1, 2);
this.overArchingTableLayoutPanel.Controls.Add(this.addRemoveTableLayoutPanel, 0, 3);
this.overArchingTableLayoutPanel.Controls.Add(this.propertiesLabel, 2, 0);
this.overArchingTableLayoutPanel.Controls.Add(this.membersLabel, 0, 0);
this.overArchingTableLayoutPanel.Controls.Add(this.listbox, 0, 1);
this.overArchingTableLayoutPanel.Controls.Add(this.propertyBrowser, 2, 1);
this.overArchingTableLayoutPanel.Controls.Add(this.okCancelTableLayoutPanel, 0, 4);
this.overArchingTableLayoutPanel.Controls.Add(this.upButton, 1, 1);
this.overArchingTableLayoutPanel.Name = "overArchingTableLayoutPanel";
//
// addRemoveTableLayoutPanel
//
resources.ApplyResources(this.addRemoveTableLayoutPanel, "addRemoveTableLayoutPanel");
this.addRemoveTableLayoutPanel.Controls.Add(this.addButton, 0, 0);
this.addRemoveTableLayoutPanel.Controls.Add(this.removeButton, 2, 0);
this.addRemoveTableLayoutPanel.Margin = new System.Windows.Forms.Padding(0, 3, 3, 3);
this.addRemoveTableLayoutPanel.Name = "addRemoveTableLayoutPanel";
//
// CollectionEditor
//
this.AcceptButton = this.okButton;
resources.ApplyResources(this, "$this");
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelButton;
this.Controls.Add(this.overArchingTableLayoutPanel);
this.HelpButton = true;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "CollectionEditor";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.okCancelTableLayoutPanel.ResumeLayout(false);
this.okCancelTableLayoutPanel.PerformLayout();
this.overArchingTableLayoutPanel.ResumeLayout(false);
this.overArchingTableLayoutPanel.PerformLayout();
this.addRemoveTableLayoutPanel.ResumeLayout(false);
this.addRemoveTableLayoutPanel.PerformLayout();
this.ResumeLayout(false);
}
private void UpdateItemWidths(ListItem item) {
// VSWhidbey#384112: Its neither safe nor accurate to perform these width
// calculations prior to normal listbox handle creation. So we nop in this case now.
if (!listbox.IsHandleCreated) {
return;
}
using (Graphics g = listbox.CreateGraphics()) {
int old = listbox.HorizontalExtent;
if (item != null) {
int w = CalcItemWidth(g, item);
if (w > old) {
listbox.HorizontalExtent = w;
}
}
else {
int max = 0;
foreach (ListItem i in listbox.Items) {
int w = CalcItemWidth(g, i);
if (w > max) {
max = w;
}
}
listbox.HorizontalExtent = max;
}
}
}
///
///
/// This draws a row of the listbox.
///
private void Listbox_drawItem(object sender, DrawItemEventArgs e) {
if (e.Index != -1) {
ListItem item = (ListItem)listbox.Items[e.Index];
Graphics g = e.Graphics;
int c = listbox.Items.Count;
int maxC = (c > 1) ? c - 1: c;
// We add the +4 is a fudge factor...
//
SizeF sizeW = g.MeasureString(maxC.ToString(CultureInfo.CurrentCulture), listbox.Font);
int charactersInNumber = ((int)(Math.Log((double)maxC) / LOG10) + 1);// Luckily, this is never called if count = 0
int w = 4 + charactersInNumber * (Font.Height / 2);
w = Math.Max(w, (int)Math.Ceiling(sizeW.Width));
w += SystemInformation.BorderSize.Width * 4;
Rectangle button = new Rectangle(e.Bounds.X, e.Bounds.Y, w, e.Bounds.Height);
ControlPaint.DrawButton(g, button, ButtonState.Normal);
button.Inflate(-SystemInformation.BorderSize.Width*2, -SystemInformation.BorderSize.Height*2);
int offset = w;
Color backColor = SystemColors.Window;
Color textColor = SystemColors.WindowText;
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) {
backColor = SystemColors.Highlight;
textColor = SystemColors.HighlightText;
}
Rectangle res = new Rectangle(e.Bounds.X + offset, e.Bounds.Y,
e.Bounds.Width - offset,
e.Bounds.Height);
g.FillRectangle(new SolidBrush(backColor), res);
if ((e.State & DrawItemState.Focus) == DrawItemState.Focus) {
ControlPaint.DrawFocusRectangle(g, res);
}
offset+=2;
if (item.Editor != null && item.Editor.GetPaintValueSupported()) {
Rectangle baseVar = new Rectangle(e.Bounds.X + offset, e.Bounds.Y + 1, PAINT_WIDTH, e.Bounds.Height - 3);
g.DrawRectangle(SystemPens.ControlText, baseVar.X, baseVar.Y, baseVar.Width - 1, baseVar.Height - 1);
baseVar.Inflate(-1, -1);
item.Editor.PaintValue(item.Value, g, baseVar);
offset += PAINT_INDENT + TEXT_INDENT;
}
StringFormat format = new StringFormat();
try {
format.Alignment = StringAlignment.Center;
g.DrawString(e.Index.ToString(CultureInfo.CurrentCulture), Font, SystemBrushes.ControlText,
new Rectangle(e.Bounds.X, e.Bounds.Y, w, e.Bounds.Height), format);
}
finally {
if (format != null) {
format.Dispose();
}
}
Brush textBrush = new SolidBrush(textColor);
string itemText = GetDisplayText(item);
try {
g.DrawString(itemText, Font, textBrush,
new Rectangle(e.Bounds.X + offset, e.Bounds.Y, e.Bounds.Width - offset, e.Bounds.Height));
}
finally {
if (textBrush != null) {
textBrush.Dispose();
}
}
// Check to see if we need to change the horizontal extent of the listbox
//
int width = offset + (int)g.MeasureString(itemText, Font).Width;
if (width > e.Bounds.Width && listbox.HorizontalExtent < width) {
listbox.HorizontalExtent = width;
}
}
}
///
///
/// Handles keypress events for the list box.
///
private void Listbox_keyDown(object sender, KeyEventArgs kevent) {
switch (kevent.KeyData) {
case Keys.Delete:
PerformRemove();
break;
case Keys.Insert:
PerformAdd();
break;
}
}
///
///
/// Event that fires when the selected list box index changes.
///
private void Listbox_selectedIndexChanged(object sender, EventArgs e) {
UpdateEnabled();
}
///
///
/// Event that fires when the list box's window handle is created.
///
private void Listbox_handleCreated(object sender, EventArgs e) {
// VSWhidbey#384112: Since we no longer perform width calculations prior to handle
// creation now, we need to ensure we do it at least once after handle creation.
UpdateItemWidths(null);
}
///
///
/// Commits the changes to the editor.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
private void OKButton_click(object sender, EventArgs e) {
try {
if (!dirty || !CollectionEditable) {
dirty = false;
DialogResult = DialogResult.Cancel;
return;
}
// Now apply the changes to the actual value.
//
if (dirty) {
object[] items = new object[listbox.Items.Count];
for (int i = 0; i < items.Length; i++) {
items[i] = ((ListItem)listbox.Items[i]).Value;
}
Items = items;
}
// Now destroy any existing items we had.
//
if (removedItems != null && dirty) {
object[] deadItems = removedItems.ToArray();
for (int i=0; i
///
/// reflect any change events to the instance object
///
private void OnComponentChanged(object sender, ComponentChangedEventArgs e) {
// see if this is any of the items in our list...this can happen if
// we launched a child editor
if (!dirty) {
foreach (object item in originalItems) {
if (item == e.Component) {
dirty = true;
break;
}
}
}
}
///
///
/// This is called when the value property in the CollectionForm has changed.
/// In it you should update your user interface to reflect the current value.
///
protected override void OnEditValueChanged() {
// Remember these contents for cancellation
if (originalItems == null) {
originalItems = new ArrayList();
}
originalItems.Clear();
// Now update the list box.
//
listbox.Items.Clear();
propertyBrowser.Site = new PropertyGridSite(Context, propertyBrowser);
if (EditValue != null) {
SuspendEnabledUpdates();
try {
object[] items = Items;
for (int i = 0; i < items.Length; i++) {
listbox.Items.Add(new ListItem(editor, items[i]));
originalItems.Add(items[i]);
}
if (listbox.Items.Count > 0) {
listbox.SelectedIndex = 0;
}
}
finally {
ResumeEnabledUpdates(true);
}
}
else {
UpdateEnabled();
}
AdjustListBoxItemHeight();
UpdateItemWidths(null);
}
protected override void OnFontChanged(EventArgs e) {
base.OnFontChanged(e);
AdjustListBoxItemHeight();
}
///
/// Performs the actual add of new items. This is invoked by the
/// add button as well as the insert key on the list box.
///
private void PerformAdd() {
CreateAndAddInstance(NewItemTypes[0]);
}
///
/// Performs a remove by deleting all items currently selected in
/// the list box. This is called by the delete button as well as
/// the delete key on the list box.
///
private void PerformRemove() {
int index = listbox.SelectedIndex;
if (index != -1) {
SuspendEnabledUpdates();
try {
// single object selected or multiple ?
if(listbox.SelectedItems.Count > 1) {
ArrayList toBeDeleted = new ArrayList(listbox.SelectedItems);
foreach (ListItem item in toBeDeleted)
{
RemoveInternal(item);
}
} else {
RemoveInternal((ListItem)listbox.SelectedItem);
}
// set the new selected index
if (index < listbox.Items.Count) {
listbox.SelectedIndex = index;
}
else if (listbox.Items.Count > 0) {
listbox.SelectedIndex = listbox.Items.Count - 1;
}
}
finally {
ResumeEnabledUpdates(true);
}
}
}
///
///
/// When something in the properties window changes, we update pertinent text here.
///
private void PropertyGrid_propertyValueChanged(object sender, PropertyValueChangedEventArgs e) {
dirty = true;
// Refresh selected listbox item so that it picks up any name change
SuspendEnabledUpdates();
try {
listbox.RefreshItem(listbox.SelectedIndex);
}
finally {
ResumeEnabledUpdates(false);
}
// if a property changes, invalidate the grid in case
// it affects the item's name.
UpdateItemWidths(null);
listbox.Invalidate();
// also update the string above the grid.
propertiesLabel.Text = SR.GetString(SR.CollectionEditorProperties, GetDisplayText((ListItem)listbox.SelectedItem));
}
///
///
/// Used to actually remove the items, one by one.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
private void RemoveInternal(ListItem item) {
if (item != null) {
editor.OnItemRemoving(item.Value);
dirty = true;
//ListItem item = (ListItem)listbox.Items[index];
if (createdItems != null && createdItems.Contains(item.Value)) {
DestroyInstance(item.Value);
createdItems.Remove(item.Value);
listbox.Items.Remove(item);
}
else {
try {
if (CanRemoveInstance(item.Value)) {
if (removedItems == null) {
removedItems = new ArrayList();
}
removedItems.Add(item.Value);
listbox.Items.Remove(item);
} else {
throw new Exception(SR.GetString(SR.CollectionEditorCantRemoveItem, GetDisplayText(item)));
}
}
catch (Exception ex) {
DisplayError(ex);
}
}
// othewise go through the entire list
UpdateItemWidths(null);
}
}
///
///
/// Removes the selected item.
///
private void RemoveButton_click(object sender, EventArgs e) {
PerformRemove();
// enabling/disabling the buttons has moved the focus to the OK button, move it back to the sender
Control ctrlSender = (Control)sender;
if(ctrlSender.Enabled) {
ctrlSender.Focus ();
}
}
///
/// used to prevent flicker when playing with the list box selection
/// call resume when done. Calls to UpdateEnabled will return silently until Resume is called
///
private void ResumeEnabledUpdates(bool updateNow){
suspendEnabledCount--;
Debug.Assert(suspendEnabledCount >= 0, "Mismatch suspend/resume enabled");
if (updateNow) {
UpdateEnabled();
}
else {
this.BeginInvoke(new MethodInvoker(this.UpdateEnabled));
}
}
///
/// used to prevent flicker when playing with the list box selection
/// call resume when done. Calls to UpdateEnabled will return silently until Resume is called
///
private void SuspendEnabledUpdates(){
suspendEnabledCount++;
}
///
///
///
/// Called to show the dialog via the IWindowsFormsEditorService
///
///
protected internal override DialogResult ShowEditorDialog(IWindowsFormsEditorService edSvc) {
IComponentChangeService cs = null;
DialogResult result = DialogResult.OK;
try {
cs = (IComponentChangeService)editor.Context.GetService(typeof(IComponentChangeService));
if (cs != null) {
cs.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged);
}
// This is cached across requests, so reset the initial focus.
ActiveControl = listbox;
//SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.OnSysColorChange);
result = base.ShowEditorDialog(edSvc);
//SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.OnSysColorChange);
}
finally{
if (cs != null) {
cs.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
return result;
}
///
///
/// Moves an item up one in the list box.
///
private void UpButton_click(object sender, EventArgs e) {
int index = listbox.SelectedIndex;
if (index == 0)
return;
dirty = true;
try {
SuspendEnabledUpdates();
int ti = listbox.TopIndex;
object itemMove = listbox.Items[index];
listbox.Items[index] = listbox.Items[index-1];
listbox.Items[index-1] = itemMove;
if (ti > 0)
listbox.TopIndex = ti - 1;
listbox.ClearSelected();
listbox.SelectedIndex = index - 1;
// enabling/disabling the buttons has moved the focus to the OK button, move it back to the sender
Control ctrlSender = (Control)sender;
if (ctrlSender.Enabled) {
ctrlSender.Focus ();
}
}
finally {
ResumeEnabledUpdates(true);
}
}
///
///
/// Updates the set of enabled buttons.
///
private void UpdateEnabled() {
if (suspendEnabledCount > 0) {
// We're in the midst of a suspend/resume block Resume should call us back.
return;
}
bool editEnabled = (listbox.SelectedItem != null) && this.CollectionEditable;
removeButton.Enabled = editEnabled && AllowRemoveInstance(((ListItem) listbox.SelectedItem).Value);
upButton.Enabled = editEnabled && listbox.Items.Count > 1;
downButton.Enabled = editEnabled && listbox.Items.Count > 1;
propertyBrowser.Enabled = editEnabled;
addButton.Enabled = this.CollectionEditable;
if (listbox.SelectedItem != null) {
object[] items;
// If we are to create new instances from the items, then we must wrap them in an outer object.
// otherwise, the user will be presented with a batch of read only properties, which isn't terribly
// useful.
//
if (IsImmutable) {
items = new object[] {new SelectionWrapper(CollectionType, CollectionItemType, listbox, listbox.SelectedItems)};
}
else {
items = new object[listbox.SelectedItems.Count];
for (int i = 0; i < items.Length; i++) {
items[i] = ((ListItem)listbox.SelectedItems[i]).Value;
}
}
int selectedItemCount = listbox.SelectedItems.Count;
if ((selectedItemCount == 1) || (selectedItemCount == -1)) {
// handle both single select listboxes and a single item selected in a multi-select listbox
propertiesLabel.Text = SR.GetString(SR.CollectionEditorProperties, GetDisplayText((ListItem)listbox.SelectedItem));
}
else {
propertiesLabel.Text = SR.GetString(SR.CollectionEditorPropertiesMultiSelect);
}
if (editor.IsAnyObjectInheritedReadOnly(items)) {
propertyBrowser.SelectedObjects = null;
propertyBrowser.Enabled = false;
removeButton.Enabled = false;
upButton.Enabled = false;
downButton.Enabled = false;
propertiesLabel.Text = SR.GetString(SR.CollectionEditorInheritedReadOnlySelection);
}
else {
propertyBrowser.Enabled = true;
propertyBrowser.SelectedObjects = items;
}
}
else {
propertiesLabel.Text = SR.GetString(SR.CollectionEditorPropertiesNone);
propertyBrowser.SelectedObject = null;
}
}
///
///
/// This class implements a custom type descriptor that is used to provide properties for the set of
/// selected items in the collection editor. It provides a single property that is equivalent
/// to the editor's collection item type.
///
private class SelectionWrapper : PropertyDescriptor, ICustomTypeDescriptor {
private Type collectionType;
private Type collectionItemType;
private Control control;
private ICollection collection;
private PropertyDescriptorCollection properties;
private object value;
public SelectionWrapper(Type collectionType, Type collectionItemType, Control control, ICollection collection) :
base("Value",
new Attribute[] {new CategoryAttribute(collectionItemType.Name)}
) {
this.collectionType = collectionType;
this.collectionItemType = collectionItemType;
this.control = control;
this.collection = collection;
this.properties = new PropertyDescriptorCollection(new PropertyDescriptor[] {this});
Debug.Assert(collection.Count > 0, "We should only be wrapped if there is a selection");
value = this;
// In a multiselect case, see if the values are different. If so,
// NULL our value to represent indeterminate.
//
foreach (ListItem li in collection) {
if (value == this) {
value = li.Value;
}
else {
object nextValue = li.Value;
if (value != null) {
if (nextValue == null) {
value = null;
break;
}
else {
if (!value.Equals(nextValue)) {
value = null;
break;
}
}
}
else {
if (nextValue != null) {
value = null;
break;
}
}
}
}
}
///
///
///
/// When overridden in a derived class, gets the type of the
/// component this property
/// is bound to.
///
///
public override Type ComponentType {
get {
return collectionType;
}
}
///
///
///
/// When overridden in
/// a derived class, gets a value
/// indicating whether this property is read-only.
///
///
public override bool IsReadOnly {
get {
return false;
}
}
///
///
///
/// When overridden in a derived class,
/// gets the type of the property.
///
///
public override Type PropertyType {
get {
return collectionItemType;
}
}
///
///
///
/// When overridden in a derived class, indicates whether
/// resetting the will change the value of the
/// .
///
///
public override bool CanResetValue(object component) {
return false;
}
///
///
///
/// When overridden in a derived class, gets the current
/// value
/// of the
/// property on a component.
///
///
public override object GetValue(object component) {
return value;
}
///
///
///
/// When overridden in a derived class, resets the
/// value
/// for this property
/// of the component.
///
///
public override void ResetValue(object component) {
}
///
///
///
/// When overridden in a derived class, sets the value of
/// the component to a different value.
///
///
public override void SetValue(object component, object value) {
this.value = value;
foreach(ListItem li in collection) {
li.Value = value;
}
control.Invalidate();
OnValueChanged(component, EventArgs.Empty);
}
///
///
///
/// When overridden in a derived class, indicates whether the
/// value of
/// this property needs to be persisted.
///
///
public override bool ShouldSerializeValue(object component) {
return false;
}
///
///
/// Retrieves an array of member attributes for the given object.
///
AttributeCollection ICustomTypeDescriptor.GetAttributes() {
return TypeDescriptor.GetAttributes(collectionItemType);
}
///
///
/// Retrieves the class name for this object. If null is returned,
/// the type name is used.
///
string ICustomTypeDescriptor.GetClassName() {
return collectionItemType.Name;
}
///
///
/// Retrieves the name for this object. If null is returned,
/// the default is used.
///
string ICustomTypeDescriptor.GetComponentName() {
return null;
}
///
///
/// Retrieves the type converter for this object.
///
TypeConverter ICustomTypeDescriptor.GetConverter() {
return null;
}
///
///
/// Retrieves the default event.
///
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() {
return null;
}
///
///
/// Retrieves the default property.
///
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() {
return this;
}
///
///
/// Retrieves the an editor for this object.
///
object ICustomTypeDescriptor.GetEditor(Type editorBaseType) {
return null;
}
///
///
/// Retrieves an array of events that the given component instance
/// provides. This may differ from the set of events the class
/// provides. If the component is sited, the site may add or remove
/// additional events.
///
EventDescriptorCollection ICustomTypeDescriptor.GetEvents() {
return EventDescriptorCollection.Empty;
}
///
///
/// Retrieves an array of events that the given component instance
/// provides. This may differ from the set of events the class
/// provides. If the component is sited, the site may add or remove
/// additional events. The returned array of events will be
/// filtered by the given set of attributes.
///
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) {
return EventDescriptorCollection.Empty;
}
///
///
/// Retrieves an array of properties that the given component instance
/// provides. This may differ from the set of properties the class
/// provides. If the component is sited, the site may add or remove
/// additional properties.
///
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() {
return properties;
}
///
///
/// Retrieves an array of properties that the given component instance
/// provides. This may differ from the set of properties the class
/// provides. If the component is sited, the site may add or remove
/// additional properties. The returned array of properties will be
/// filtered by the given set of attributes.
///
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) {
return properties;
}
///
///
/// Retrieves the object that directly depends on this value being edited. This is
/// generally the object that is required for the PropertyDescriptor's GetValue and SetValue
/// methods. If 'null' is passed for the PropertyDescriptor, the ICustomComponent
/// descripotor implemementation should return the default object, that is the main
/// object that exposes the properties and attributes,
///
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) {
return this;
}
}
///
///
/// ListItem class. This is a single entry in our list box. It contains the value we're editing
/// as well as accessors for the type converter and UI editor.
///
private class ListItem {
private object value;
private object uiTypeEditor;
private CollectionEditor parentCollectionEditor;
public ListItem(CollectionEditor parentCollectionEditor, object value) {
this.value = value;
this.parentCollectionEditor = parentCollectionEditor;
}
public override string ToString() {
return parentCollectionEditor.GetDisplayText(this.value);
}
public UITypeEditor Editor {
get {
if (uiTypeEditor == null) {
uiTypeEditor = TypeDescriptor.GetEditor(value, typeof(UITypeEditor));
if (uiTypeEditor == null) {
uiTypeEditor = this;
}
}
if (uiTypeEditor != this) {
return (UITypeEditor) uiTypeEditor;
}
return null;
}
}
public object Value {
get {
return value;
}
set {
uiTypeEditor = null;
this.value = value;
}
}
}
///
///
/// Menu items we attach to the drop down menu if there are multiple
/// types the collection editor can create.
///
private class TypeMenuItem : ToolStripMenuItem {
Type itemType;
public TypeMenuItem(Type itemType, EventHandler handler) :
base(itemType.Name, null, handler) {
this.itemType = itemType;
}
public Type ItemType {
get {
return itemType;
}
}
}
}
///
///
/// List box filled with ListItem objects representing the collection.
///
internal class FilterListBox : ListBox {
private PropertyGrid grid;
private Message lastKeyDown;
private PropertyGrid PropertyGrid {
get {
if (grid == null) {
foreach (Control c in Parent.Controls) {
if (c is PropertyGrid) {
grid = (PropertyGrid)c;
break;
}
}
}
return grid;
}
}
// Expose the protected RefreshItem() method so that CollectionEditor can use it
public new void RefreshItem(int index) {
base.RefreshItem(index);
}
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case NativeMethods.WM_KEYDOWN:
this.lastKeyDown = m;
// the first thing the ime does on a key it cares about is send a VK_PROCESSKEY,
// so we use that to sling focus to the grid.
//
if ((int)m.WParam == NativeMethods.VK_PROCESSKEY) {
if (PropertyGrid != null) {
PropertyGrid.Focus();
UnsafeNativeMethods.SetFocus(new HandleRef(PropertyGrid, PropertyGrid.Handle));
Application.DoEvents();
}
else {
break;
}
if(PropertyGrid.Focused || PropertyGrid.ContainsFocus) {
// recreate the keystroke to the newly activated window
NativeMethods.SendMessage(UnsafeNativeMethods.GetFocus(), NativeMethods.WM_KEYDOWN, lastKeyDown.WParam, lastKeyDown.LParam);
}
}
break;
case NativeMethods.WM_CHAR:
if ((Control.ModifierKeys & (Keys.Control | Keys.Alt)) != 0) {
break;
}
if (PropertyGrid != null) {
PropertyGrid.Focus();
UnsafeNativeMethods.SetFocus(new HandleRef(PropertyGrid, PropertyGrid.Handle));
Application.DoEvents();
}
else {
break;
}
// Make sure we changed focus properly
// recreate the keystroke to the newly activated window
//
if (PropertyGrid.Focused || PropertyGrid.ContainsFocus) {
IntPtr hWnd = UnsafeNativeMethods.GetFocus();
NativeMethods.SendMessage(hWnd, NativeMethods.WM_KEYDOWN, lastKeyDown.WParam, lastKeyDown.LParam);
NativeMethods.SendMessage(hWnd, NativeMethods.WM_CHAR, m.WParam, m.LParam);
return;
}
break;
}
base.WndProc(ref m);
}
}
///
///
///
/// The
/// provides a modal dialog for editing the
/// contents of a collection.
///
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors")] //breaking change
protected abstract class CollectionForm : Form {
// Manipulation of the collection.
//
private CollectionEditor editor;
private object value;
private short editableState = EditableDynamic;
private const short EditableDynamic = 0;
private const short EditableYes = 1;
private const short EditableNo = 2;
///
///
///
/// Initializes a new instance of the class.
///
///
public CollectionForm(CollectionEditor editor) {
this.editor = editor;
}
///
///
///
/// Gets or sets the data type of each item in the collection.
///
///
protected Type CollectionItemType {
get {
return editor.CollectionItemType;
}
}
///
///
///
/// Gets or sets the type of the collection.
///
///
protected Type CollectionType {
get {
return editor.CollectionType;
}
}
///
internal virtual bool CollectionEditable {
get {
if (editableState != EditableDynamic) {
return editableState == EditableYes;
}
bool editable = typeof(IList).IsAssignableFrom(editor.CollectionType);
if (editable) {
IList list = EditValue as IList;
if (list != null) {
return !list.IsReadOnly;
}
}
return editable;
}
set {
if (value) {
editableState = EditableYes;
}
else {
editableState = EditableNo;
}
}
}
///
///
///
/// Gets or sets a type descriptor that indicates the current context.
///
///
protected ITypeDescriptorContext Context {
get {
return editor.Context;
}
}
///
///
/// Gets or sets the value of the item being edited.
///
public object EditValue {
get {
return value;
}
set {
this.value = value;
OnEditValueChanged();
}
}
///
///
///
/// Gets or sets the
/// array of items this form is to display.
///
///
protected object[] Items {
get {
return editor.GetItems(EditValue);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
set {
// Request our desire to make a change.
//
bool canChange = false;
try {
canChange = Context.OnComponentChanging();
} catch (Exception ex) {
if(!ClientUtils.IsCriticalException(ex)) {
DisplayError(ex);
} else {
throw;
}
}
if (canChange) {
object newValue = editor.SetItems(EditValue, value);
if (newValue != EditValue) {
EditValue = newValue;
}
Context.OnComponentChanged();
}
}
}
///
///
///
/// Gets or sets the available item types that can be created for this
/// collection.
///
///
protected Type[] NewItemTypes {
get {
return editor.NewItemTypes;
}
}
///
///
/// Gets or sets a value indicating whether original members of the collection
/// can be removed.
///
protected bool CanRemoveInstance(object value) {
return editor.CanRemoveInstance(value);
}
///
///
/// Gets or sets a value indicating whether multiple collection members can be
/// selected.
///
protected virtual bool CanSelectMultipleInstances() {
return editor.CanSelectMultipleInstances();
}
///
///
///
/// Creates a new instance of the specified collection item type.
///
///
protected object CreateInstance(Type itemType) {
return editor.CreateInstance(itemType);
}
///
///
///
/// Destroys the specified instance of the object.
///
///
protected void DestroyInstance(object instance) {
editor.DestroyInstance(instance);
}
///
///
/// Displays the given exception to the user.
///
protected virtual void DisplayError(Exception e) {
IUIService uis = (IUIService)GetService(typeof(IUIService));
if (uis != null) {
uis.ShowError(e);
}
else {
string message = e.Message;
if (message == null || message.Length == 0) {
message = e.ToString();
}
RTLAwareMessageBox.Show(null, message, null, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1, 0);
}
}
///
///
///
/// Gets the requested service, if it is available.
///
///
protected override object GetService(Type serviceType) {
return editor.GetService(serviceType);
}
///
///
///
/// Called to show the dialog via the IWindowsFormsEditorService
///
///
protected internal virtual DialogResult ShowEditorDialog(IWindowsFormsEditorService edSvc) {
return edSvc.ShowDialog(this);
}
///
///
///
/// This is called when the value property in
/// the
/// has changed.
///
///
protected abstract void OnEditValueChanged();
}
internal class PropertyGridSite : ISite {
private IServiceProvider sp;
private IComponent comp;
private bool inGetService = false;
public PropertyGridSite(IServiceProvider sp, IComponent comp) {
this.sp = sp;
this.comp = comp;
}
/** The component sited by this component site. */
///
///
/// When implemented by a class, gets the component associated with the .
///
public IComponent Component {get {return comp;}}
/** The container in which the component is sited. */
///
///
/// When implemented by a class, gets the container associated with the .
///
public IContainer Container {get {return null;}}
/** Indicates whether the component is in design mode. */
///
///
/// When implemented by a class, determines whether the component is in design mode.
///
public bool DesignMode {get {return false;}}
/**
* The name of the component.
*/
///
///
/// When implemented by a class, gets or sets the name of
/// the component associated with the .
///
public String Name {
get {return null;}
set {}
}
public object GetService(Type t) {
if (!inGetService && sp != null) {
try {
inGetService = true;
return sp.GetService(t);
}
finally {
inGetService = false;
}
}
return null;
}
}
}
}
// 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
- EncryptedData.cs
- CqlQuery.cs
- Base64Encoding.cs
- HttpModuleAction.cs
- DateTimeStorage.cs
- CellPartitioner.cs
- ScrollChrome.cs
- Vector3dCollection.cs
- XmlSchemaImport.cs
- SqlRowUpdatingEvent.cs
- CustomErrorsSection.cs
- TextBounds.cs
- XmlComment.cs
- EntityDataSourceStatementEditorForm.cs
- ClientSponsor.cs
- DynamicFilter.cs
- BitmapEditor.cs
- SoapConverter.cs
- WebConfigurationManager.cs
- PublisherMembershipCondition.cs
- TextStore.cs
- Empty.cs
- MDIControlStrip.cs
- XamlClipboardData.cs
- PasswordBoxAutomationPeer.cs
- SendKeys.cs
- SelectedCellsChangedEventArgs.cs
- DataGridViewColumnConverter.cs
- ADMembershipProvider.cs
- WebPartConnectionsConnectVerb.cs
- VectorAnimationBase.cs
- UIPermission.cs
- ManifestResourceInfo.cs
- TreeNodeCollection.cs
- AvTrace.cs
- DetailsViewRowCollection.cs
- PageThemeParser.cs
- SourceFilter.cs
- AsymmetricKeyExchangeDeformatter.cs
- ToolStripManager.cs
- ErrorHandler.cs
- ProfileGroupSettingsCollection.cs
- DataControlFieldHeaderCell.cs
- DrawingContextWalker.cs
- webclient.cs
- CodeGroup.cs
- DecoratedNameAttribute.cs
- NativeMethods.cs
- JsonServiceDocumentSerializer.cs
- OleDbRowUpdatedEvent.cs
- ObjectSecurity.cs
- XmlDataSourceView.cs
- Point3DCollection.cs
- TypeHelpers.cs
- Parameter.cs
- QueryAsyncResult.cs
- BufferModesCollection.cs
- ProvideValueServiceProvider.cs
- RegisteredArrayDeclaration.cs
- BaseHashHelper.cs
- XmlConvert.cs
- Not.cs
- DSACryptoServiceProvider.cs
- AsyncPostBackErrorEventArgs.cs
- TextElementEnumerator.cs
- RequestTimeoutManager.cs
- SqlError.cs
- TextElementEnumerator.cs
- RemotingConfigParser.cs
- TimeSpan.cs
- OperatingSystem.cs
- SecondaryIndex.cs
- ComponentManagerBroker.cs
- XPathNavigatorReader.cs
- ContainerCodeDomSerializer.cs
- PLINQETWProvider.cs
- PeerApplicationLaunchInfo.cs
- SqlDataReader.cs
- RandomNumberGenerator.cs
- XmlWhitespace.cs
- TemplateControlParser.cs
- ZipFileInfo.cs
- CreateUserWizard.cs
- FormViewRow.cs
- DataSourceDescriptorCollection.cs
- DoubleAnimation.cs
- CollectionBuilder.cs
- WinFormsComponentEditor.cs
- RotationValidation.cs
- Component.cs
- FormattedTextSymbols.cs
- ExtendedPropertyDescriptor.cs
- IndexedSelectQueryOperator.cs
- ReadOnlyHierarchicalDataSourceView.cs
- DetailsViewDeletedEventArgs.cs
- FileUtil.cs
- DataGridCaption.cs
- XmlSchemaObjectCollection.cs
- UnmanagedBitmapWrapper.cs
- UiaCoreApi.cs