Exemplo n.º 1
0
        /// <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);
        }
Exemplo n.º 2
0
        /// <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;
        }