/// <summary> /// Required UITypeEditor override. Opens dropdown modally /// and waits for user input. /// </summary> /// <param name="context">The descriptor context. Used to retrieve /// the live instance and other data.</param> /// <param name="provider">The service provider for the given context.</param> /// <param name="value">The current property value</param> /// <returns>The updated property value, or the orignal value to effect a cancel</returns> public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { IWindowsFormsEditorService editor = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService; if (editor != null) { object newObject = value; // Get the tree contents ITree tree = GetTree(context, value); // Proceed if there is anything to show // Don't check tree.VisibleItemCount. Allows the derived class to display an empty dropdown // by returning a tree with no visible elements. if (tree != null) { // Create a listbox with its events using (DropDownTreeControl treeControl = new DropDownTreeControl()) { if (UseStandardCheckBoxes) { ImageList images = new ImageList(); images.ImageSize = new Size(16, 16); treeControl.ImageList = images; treeControl.StandardCheckBoxes = true; } treeControl.BindingContextChanged += new EventHandler(HandleBindingContextChanged); treeControl.AfterDoubleClick += delegate(object sender, DoubleClickEventArgs e) { if (e.Button == MouseButtons.Left) { editor.CloseDropDown(); } }; // Manage the size of the control Size lastSize = LastControlSize; if (!lastSize.IsEmpty) { treeControl.Size = lastSize; } myInitialSelectionValue = value; // Show the dropdown. This is modal. IMultiColumnTree multiTree = tree as IMultiColumnTree; if (multiTree != null) { treeControl.MultiColumnTree = multiTree; } else { #if VISUALSTUDIO_9_0 // MSBUG: Hack workaround crashing bug in VirtualTreeControl.OnToggleExpansion treeControl.ColumnPermutation = new ColumnPermutation(1, new int[] { 0 }, false); #endif treeControl.Tree = tree; } Control adornedControl = SetTreeControlDisplayOptions(treeControl) ?? treeControl; bool escapePressed = false; EditorUtility.AttachEscapeKeyPressedEventHandler( adornedControl, delegate(object sender, EventArgs e) { escapePressed = true; }); // Make sure keystrokes are not forwarded while the modal dropdown is open IVirtualTreeInPlaceControl virtualTreeInPlaceControl = editor as IVirtualTreeInPlaceControl; VirtualTreeInPlaceControlFlags flags = virtualTreeInPlaceControl != null ? virtualTreeInPlaceControl.Flags : 0; if (0 != (flags & VirtualTreeInPlaceControlFlags.ForwardKeyEvents)) { virtualTreeInPlaceControl.Flags = flags & ~VirtualTreeInPlaceControlFlags.ForwardKeyEvents; } editor.DropDownControl(adornedControl); // Restore keystroke forwarding if (0 != (flags & VirtualTreeInPlaceControlFlags.ForwardKeyEvents)) { virtualTreeInPlaceControl.Flags = flags; } // Record the final size, we'll use it next time for this type of control LastControlSize = treeControl.Size; // Make sure the user didn't cancel, and give derived classes a chance // to translate the value displayed in the tree to an appropriately // typed value for the associated property. if (!escapePressed) { int lastRow = treeControl.LastSelectedRow; int lastColumn = treeControl.LastSelectedColumn; if (lastRow != -1 || AlwaysTranslateToValue) { newObject = TranslateToValue(context, value, tree, lastRow, lastColumn); } } } } return(newObject); } return(value); }
/// <summary> /// Required UITypeEditor override. Opens dropdown modally /// and waits for user input. /// </summary> /// <param name="context">The descriptor context. Used to retrieve /// the live instance and other data.</param> /// <param name="provider">The service provider for the given context.</param> /// <param name="value">The current property value</param> /// <returns>The updated property value, or the orignal value to effect a cancel</returns> public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { IWindowsFormsEditorService editor = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService; if (editor != null) { object newObject = value; // Get the list contents and add a null handler if needed IList elements = GetContentList(context, value); string nullText = NullItemText; // Proceed if there is anything to show if (nullText != null || (elements != null && elements.Count != 0)) { // Create a tree control using (DropDownTreeControl treeControl = new DropDownTreeControl()) { #if VISUALSTUDIO_9_0 // MSBUG: Hack workaround crashing bug in VirtualTreeControl.OnToggleExpansion treeControl.ColumnPermutation = new ColumnPermutation(1, new int[] { 0 }, false); #endif // Close the dropdown after a double click treeControl.AfterDoubleClick += delegate(object sender, DoubleClickEventArgs e) { if (e.Button == MouseButtons.Left) { editor.CloseDropDown(); } }; // Create a tree for the control ITree tree = new VirtualTree(); tree.Root = new SimpleBranch(this, elements, nullText); treeControl.Tree = tree; // Manage the size of the control Size lastSize = LastControlSize; if (!lastSize.IsEmpty) { treeControl.Size = lastSize; } int initialIndex = -1; if (value != null) { if (elements != null) { initialIndex = elements.IndexOf(TranslateToDisplayObject(value, elements)); if (nullText != null) { ++initialIndex; } } } else if (nullText != null) { initialIndex = 0; } if (initialIndex != -1) { treeControl.InitialSelectionIndex = initialIndex; } Control adornedControl = SetTreeControlDisplayOptions(treeControl) ?? treeControl; bool escapePressed = false; EditorUtility.AttachEscapeKeyPressedEventHandler( adornedControl, delegate(object sender, EventArgs e) { escapePressed = true; }); // Make sure keystrokes are not forwarded while the modal dropdown is open IVirtualTreeInPlaceControl virtualTreeInPlaceControl = editor as IVirtualTreeInPlaceControl; VirtualTreeInPlaceControlFlags flags = virtualTreeInPlaceControl != null ? virtualTreeInPlaceControl.Flags : 0; if (0 != (flags & VirtualTreeInPlaceControlFlags.ForwardKeyEvents)) { virtualTreeInPlaceControl.Flags = flags & ~VirtualTreeInPlaceControlFlags.ForwardKeyEvents; } // Show the dropdown. This is modal. editor.DropDownControl(adornedControl); // Restore keystroke forwarding if (0 != (flags & VirtualTreeInPlaceControlFlags.ForwardKeyEvents)) { virtualTreeInPlaceControl.Flags = flags; } // Record the final size, we'll use it next time for this type of control LastControlSize = treeControl.Size; // Make sure the user didn't cancel, and translate the null placeholder // back to null if necessary if (!escapePressed) { int lastIndex = treeControl.AnchorIndex; if (lastIndex != -1) { if (nullText != null) { --lastIndex; if (lastIndex == -1) { newObject = null; } else { newObject = elements[lastIndex]; } } else { newObject = elements[lastIndex]; } // Give the caller the chance to change the type of the chosen object newObject = TranslateFromDisplayObject(lastIndex, newObject); } } } } return(newObject); } return(value); }
private bool DismissLabelEdit(bool fCancel, bool fForceFocus) { if (GetStateFlag(VTCStateFlags.NoDismissEdit)) { return false; } var fOkToContinue = true; var edit = myInPlaceControl; if (edit == null) { // Also make sure there are no pending edits... CancelEditTimer(); return true; } var editCtl = edit.InPlaceControl; // Assume that if we are not visible that the window is in the // process of being destroyed and we should not process the // editing of the window... if (!NativeMethods.IsWindowVisible(Handle)) { fCancel = true; } // // We are using the Window Id of the control as a BOOL to // state if it is dirty or not. Debug.Assert(GetStateFlag(VTCStateFlags.LabelEditActive)); if (GetStateFlag(VTCStateFlags.LabelEditProcessing)) { // We are in the process of processing an update now, bail out return true; } else if (GetStateFlag(VTCStateFlags.LabelEditDirty)) { // The edit control is dirty so continue. SetStateFlag(VTCStateFlags.LabelEditProcessing, true); } else { // The edit control is not dirty so act like cancel. fCancel = true; SetStateFlag(VTCStateFlags.LabelEditProcessing, true); } var fCloseWindow = fCancel; var selectionColumn = -1; var iEdit = myEditIndex; // If we're canceling outright, then there is no reason to // notify the branch that the edit is being canceled. try { if (!fCancel) { // Deleting items can set myEditIndex to NullIndex if the program // deleted the items out from underneath us (while we are waiting // for the edit timer). if (iEdit != VirtualTreeConstant.NullIndex) { // Relocate the branch and item for the object and // ask them to commit. int nativeSelectionColumn; ResolveSelectionColumn(iEdit, out selectionColumn, out nativeSelectionColumn); var info = myTree.GetItemInfo(iEdit, nativeSelectionColumn, false); var editCode = (myCustomInPlaceCommit == null) ? info.Branch.CommitLabelEdit(info.Row, info.Column, editCtl.Text) : myCustomInPlaceCommit(info, editCtl); switch (editCode) { case LabelEditResult.AcceptEdit: //NYI: Need to adjust horizontal extent fCloseWindow = true; break; case LabelEditResult.CancelEdit: fCloseWindow = true; break; case LabelEditResult.BlockDeactivate: goto case LabelEditResult.CancelEdit; //NYI: Need to get a posting mechanism here, probably through //BeginInvoke, to call back and reopen the edit window at a later time. //Debug.Assert(!fCloseWindow); //SetStateFlag(VTCStateFlags.LabelEditProcessing, false); //myInPlaceControl.SelectAllText(); //break; default: Debug.Assert(false, "Invalid Enum Value"); // Nothing much to do except toss it fCloseWindow = true; break; } } else { fCloseWindow = true; } } } catch (Exception ex) { if (CriticalException.IsCriticalException(ex)) { fCloseWindow = false; // prevents us from doing work in finally block below in the case of a critical exception. throw; } fCloseWindow = true; fOkToContinue = false; if (!DisplayException(ex)) { throw; } } /* catch { fCloseWindow = true; fOkToContinue = false; throw; }*/ finally { if (fCloseWindow) { // Make sure the text redraws properly if (iEdit != VirtualTreeConstant.NullIndex && iEdit < ItemCount) { if (selectionColumn != -1) { int nativeSelectionColumn; ResolveSelectionColumn(iEdit, out selectionColumn, out nativeSelectionColumn); } InvalidateAreaForLabelEdit(iEdit, selectionColumn, editCtl); } SetStateFlag(VTCStateFlags.NoDismissEdit, true); // this is so that we don't recurse due to killfocus if (fForceFocus && !Focused) { Focus(); } editCtl.Hide(); SetStateFlag(VTCStateFlags.NoDismissEdit, false); // If we did not reenter edit mode before now reset the edit state // variables to NULL var disposeControl = (myInPlaceControl.Flags & VirtualTreeInPlaceControls.DisposeControl) != 0; if (myInPlaceControl == edit) { myInPlaceControl = null; myCustomInPlaceCommit = null; SetStateFlag(VTCStateFlags.LabelEditMask, false); myEditIndex = VirtualTreeConstant.NullIndex; } // done with the edit control -- if desired, we will // dispose of it. Otherwise, it's up to the branch if (disposeControl) { // Reset this flag, Dispose can have side effects SetStateFlag(VTCStateFlags.NoDismissEdit, true); editCtl.Dispose(); SetStateFlag(VTCStateFlags.NoDismissEdit, false); } } } // notify listeners of label edit state change OnLabelEditControlChanged(EventArgs.Empty); return fOkToContinue; }
private void SetEditInPlaceSize(IVirtualTreeInPlaceControl edit, int stringWidth, ref Rectangle boundingRect) { var editCtl = edit.InPlaceControl; var hwndEdit = editCtl.Handle; if ((edit.Flags & VirtualTreeInPlaceControls.SizeToText) != 0) { //Size stringSize = new Size(stringWidth, myItemHeight); var stringSize = new Size(stringWidth, myItemHeight); if (HasHorizontalGridLines) { stringSize.Height -= 1; } // Minimum text box size is 1/4 icon spacing size stringSize.Width = Math.Max(stringSize.Width, SystemInformation.IconSpacingSize.Width / 4); // position the text rect based on the text rect passed in // if wrapping, center the edit control around the text mid point var textRect = new Rectangle(0, 0, stringSize.Width, stringSize.Height); textRect.Offset( boundingRect.Left, boundingRect.Top + (boundingRect.Height - textRect.Height) / 2); // give a little space to ease the editing of this thing textRect.Width += edit.ExtraEditWidth; // // Make sure that the whole edit window is always visible. // We should not extend it to the outside of the parent window. // var clippedRect = Rectangle.Intersect(ClientRectangle, textRect); if (!clippedRect.IsEmpty) { textRect = clippedRect; } // // Inflate it after the clipping, because it's ok to hide border. // var rcFormat = new NativeMethods.RECT(edit.FormattingRectangle); // Turn the margins inside-out so we can AdjustWindowRect on them. rcFormat.top = -rcFormat.top; rcFormat.left = -rcFormat.left; NativeMethods.AdjustWindowRectEx( ref rcFormat, NativeMethods.GetWindowStyle(hwndEdit), false, NativeMethods.GetWindowExStyle(hwndEdit)); textRect.Inflate(-rcFormat.left, -rcFormat.top); boundingRect = textRect; } NativeMethods.HideCaret(hwndEdit); editCtl.Size = new Size(boundingRect.Width, boundingRect.Height); editCtl.Location = new Point(boundingRect.Left, boundingRect.Top); NativeMethods.InvalidateRect(hwndEdit, IntPtr.Zero, true); NativeMethods.ShowCaret(hwndEdit); }
private IVirtualTreeInPlaceControl DoLabelEdit( int absRow, int column, int message, bool explicitActivation, ref bool immediateActivation) { Debug.Assert(!explicitActivation || !immediateActivation); if (!GetAnyStyleFlag(VTCStyleFlags.LabelEditsMask)) { return null; } DismissLabelEdit(false, false); var nativeColumn = (myColumnPermutation != null) ? myColumnPermutation.GetNativeColumn(column) : column; var info = myTree.GetItemInfo(absRow, nativeColumn, false); var flags = info.Branch.Features; // UNDONE: Explicit Label Edits if (immediateActivation && (0 == (flags & (BranchFeatures.ImmediateMouseLabelEdits | BranchFeatures.ImmediateSelectionLabelEdits)))) { if (0 != (flags & BranchFeatures.DelayedLabelEdits)) { immediateActivation = false; } return null; } else if (explicitActivation && (0 == (flags & BranchFeatures.ExplicitLabelEdits))) { return null; } else if (!explicitActivation && !immediateActivation && 0 == (flags & BranchFeatures.DelayedLabelEdits)) { return null; } // Begin the label editing sequence by retrieving the data from // from the branch and filling in unsupplied default values. VirtualTreeLabelEditActivationStyles activationStyle; if (immediateActivation) { // Distinguish between mouse and non-mouse selection if the branch supports both if (0 == (flags & BranchFeatures.ImmediateMouseLabelEdits)) { activationStyle = VirtualTreeLabelEditActivationStyles.ImmediateSelection; } else if (GetStateFlag(VTCStateFlags.SelChangeFromMouse)) { activationStyle = VirtualTreeLabelEditActivationStyles.ImmediateMouse; } else { activationStyle = VirtualTreeLabelEditActivationStyles.ImmediateSelection; } } else if (explicitActivation) { activationStyle = VirtualTreeLabelEditActivationStyles.Explicit; } else { activationStyle = VirtualTreeLabelEditActivationStyles.Delayed; } var editData = info.Branch.BeginLabelEdit(info.Row, info.Column, activationStyle); if (!editData.IsValid) { return null; } else if (immediateActivation && editData.ActivationDeferred) { immediateActivation = false; return null; } var labelText = editData.AlternateText; var inPlaceEdit = editData.CustomInPlaceEdit; var maxTextLength = editData.MaxTextLength; if (labelText == null) { // Note: Don't compare to String.Empty. This allows the // user to return String.Empty to display an edit box with // nothing in it. labelText = info.Branch.GetText(info.Row, info.Column); } if (inPlaceEdit == null) // Alternate condition is not used. Without this check, // an invalid type will automatically throw a casting // exception in CreateEditInPlaceWindow, which is better // than silently ignoring the user setting. //|| !inPlaceEditType.IsSubclassOf(typeof(IVirtualTreeInPlaceControl))) { inPlaceEdit = typeof(VirtualTreeInPlaceEditControl); } ScrollIntoView(absRow, column); myEditColumn = column; myInPlaceControl = CreateEditInPlaceWindow(labelText, maxTextLength, column, message, inPlaceEdit); myCustomInPlaceCommit = editData.CustomCommit; var ctl = myInPlaceControl.InPlaceControl; // Set the colors of the in-place edit control myInPlaceControl.InPlaceControl.ForeColor = InPlaceEditForeColor; myInPlaceControl.InPlaceControl.BackColor = InPlaceEditBackColor; SetStateFlag(VTCStateFlags.LabelEditMask, false); SetStateFlag(VTCStateFlags.LabelEditActive, true); HideBubble(); myEditIndex = absRow; SetEditSize(); var invalidateEditItem = true; if (0 != (myInPlaceControl.Flags & VirtualTreeInPlaceControls.DrawItemText)) { SetStateFlag(VTCStateFlags.LabelEditTransparent, true); invalidateEditItem = false; } // Show the window and set focus to it. Do this after setting the // size so we don't get flicker. ctl.Show(); // Changing focus causes OnLostFocus for the currently focused control. // Ignore the case that this triggers a DismissLabelEdit. May happen if the edit control has // child controls, each of which dismisses on an OnLostFocus. SetStateFlag(VTCStateFlags.NoDismissEdit, true); try { ctl.Focus(); } finally { SetStateFlag(VTCStateFlags.NoDismissEdit, false); } if (invalidateEditItem) { InvalidateAreaForLabelEdit(absRow, column, ctl); } // Rescroll edit window myInPlaceControl.SelectAllText(); // notify listeners of label edit state change OnLabelEditControlChanged(EventArgs.Empty); return myInPlaceControl; }