Completing edit with an enter key press

Grids for WPF Forum

Posted 10 years ago by RS
Version: 4.5.0485
Avatar
I'd like to be able to confirm an edit in a propertygrid field by pressing enter (as in the VS property grid and most other property grids I have used). Can you give me some advice on the best way to do that? I'd like to avoid re-templating / custom code if there's something obvious that I have missed.

RS

Comments (19)

Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
You can handle the KeyPress event on the PropertyGrid and then "move" the focus when the Enter key is pressed. Moving the focus is what commits the change. Something like this would do what you want (where propgrid is a PropertyGrid):
private void propGrid_KeyDown(object sender, KeyEventArgs e) {
   if (e.Key == Key.Enter)
      this.propGrid.Focus();
   }
}
You an set the focus back to the previously focused item, if you want also. Or you mgiht be able to simply fire a LostFocus event on the "sender" object.

Hope this helps.


Actipro Software Support

Posted 10 years ago by RS
Avatar
That's very useful, thank you. Based on that information, this is the code I've come up with that seems to work in the way I want (and rolls back to the last source value if escape is pressed during an edit):

If TypeOf e.OriginalSource Is TextBox AndAlso e.Key = Key.Return Then
                ' Enter key in a TextBox - move focus back to the PropertyGridDataAccessorItem
                DirectCast(e.OriginalSource, UIElement).MoveFocus(New TraversalRequest(FocusNavigationDirection.Previous))
            ElseIf TypeOf e.OriginalSource Is TextBox AndAlso e.Key = Key.Escape Then
                ' Cancel the edit in progress
                Dim BindingExpression As BindingExpression = DirectCast(e.OriginalSource, TextBox).GetBindingExpression(TextBox.TextProperty)
                If BindingExpression IsNot Nothing Then
                    BindingExpression.UpdateTarget()
                    ' Now move focus back to the PropertyGridDataAccessorItem
                    DirectCast(e.OriginalSource, UIElement).MoveFocus(New TraversalRequest(FocusNavigationDirection.Previous))
                End If
            ElseIf TypeOf e.OriginalSource Is PropertyGridDataAccessorItem Then
                ' This is the focus item when the property has focus but an edit isn't in progress
                DirectCast(e.OriginalSource, PropertyGridDataAccessorItem).MoveFocus(New TraversalRequest(FocusNavigationDirection.Next))
            End If
What I'm trying to achieve here is an experience matching the VS property grid. It would be great if you could build in some of that standard functionality for keyboard navigation that a user familiar with other property grids might expect out of the box:

1. Start an edit when the user starts typing (replacing the existing text)
2. Commit an edit when the enter key is pressed
3. Cycle combo box selected items when double clicking on a property
4. Set focus to the textbox and select all text when F4 is pressed

It's obviously great that with Wpf and the way your controls are architected that you can customize to achieve this, but it would be event better to have it included in the out of the box experience.

P.S. I can't seem to get the code block to work in the post.

[Modified at 02/25/2009 09:52 AM]

RS

Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
I've added your request to our TODO list, but could you elaborate on #1? Is the current text selected when you start typing? Or perhaps the focus is on the name cell?


Actipro Software Support

Posted 10 years ago by RS
Avatar
If you have focus on a property (i.e. clicked on the name cell) I think that the appropriate behavior if you start typing would be for that typing to start an edit (move focus to the text box so the characters you type appear there). If the text box already has content in that case, the typed text would replace any existing content.

You can try this behavior out for yourself in Visual Studio property grid.

RS

Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
I see what you are referring to now. We can add this to the TODO list also.

This type of feature is not as straight forward with our PropertyGrid, since you can dropin any control(s) for modifying the value you want. But we should be able to handle the default cases, such as the TextBox/ComboBox.


Actipro Software Support

Posted 9 years ago by Tom P.
Avatar
Quote:
If you have focus on a property (i.e. clicked on the name cell) I think that the appropriate behavior if you start typing would be for that typing to start an edit (move focus to the text box so the characters you type appear there). If the text box already has content in that case, the typed text would replace any existing content.

You can try this behavior out for yourself in Visual Studio property grid.
RS


I'd like to 2nd this. It's driving me a bit batty at the moment. The more this control can work like Visual Studio 2008+, Expression, etc. the better.
Posted 9 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Tom,

We will see about adding this functionality for the 2010.1 release.


Actipro Software Support

Posted 9 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
FYI, We've added configurable text input forwarding support for the 2010.1 release.


Actipro Software Support

Posted 8 years ago by Kris Culin
Avatar
I am evaluating the latest version available of the WPF PropertyGrid.

I am trying to figure out how I can get the <enter> key to be used as an auto-commit like the WinForms PropertyGrid.

Also, using F4 to edit would be nice too.

Is this built-in yet or are there hoops that I need to jump through first?

Having tabbing support would be a very feature as well (if it's not already there)

The documentation on this subject is abiguous.

Kris
Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Kris,

This would have to be a function of the underlying editor control. By default we use TextBox and/or ComboBox controls and bindings that update when the focus is lost. In order to support having the Enter key commit changes, you'd have to watch for a KeyDown event on these controls then explicitly update their bindings (e.g. for TextBox you'd have to update the source of the Text property binding).

This can be done by defining a custom property editor or using implicit Styles for TextBox/ComboBox, or any other controls you may be using.

I'm not sure what you mean about "tabbing support". The Tab can be used to move around the PropertyGrid.


Actipro Software Support

Posted 8 years ago by Kris Culin
Avatar
OK, I'm not sure I follow.

This is what I'm doing.

SelectedObject is bound via XAML to my object's SelectedItem property. The propertyGrid is correctly populated with the appropriate fields when my object is selected (it happens to be part of a treeview)

I click on one of the properties [in the PG] and start typing in the new value. In this case, it's just a Label property which is a string (I assume the PG just uses a TextBlock for editing?).

I hit <enter> and nothing happens.

In the WinForms PG when you hit <enter> it will push the value to the PG and update the field.

How do I make this happen? I would think it would be easy-peasy but I can't seem to find the answer in the documentation.

My object is pretty simple. It's just a class with some properties and it implements INotifyPropertyChanged. Each property calls the PropertyChanged event in the setter.

Kris
Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Kris,

The editors in our PropertyGrid can be easily customized, unlike the WinForms version (which offers little customization). Ultimately, the "editors" in our PropertyGrid are just other WPF controls. We provide several built-in editors that work much like the WinForms editor. These built-in editors leverage the native TextBox and ComboBox, among a few other controls.

What I was referring to before, is that the TextBox/ComboBox and their Binding are responsible for committing the value changes back to the associated property. We don't currently have anything watching for the Enter key, but that can be accomplished manually. Using the code shown earlier in the thread, or the code shown below.

Either way, we've added support for committing change when the Enter key is pressed to our default property editors for the next maintenance release. We've effectively added the new attached behavior shown here:
/// <summary>
/// Provides attached behaviors for various controls that can update the associated binding source.
/// </summary>
public static class BindingSourceUpdateBehavior {

    #region Dependency Properties

    /// <summary>
    /// Identifies the <c>IsUpdatedOnEnter</c> attached dependency property.  This field is read-only.
    /// </summary>
    /// <value>The identifier for the <c>IsUpdatedOnEnter</c> attached dependency property.</value>
    public static readonly DependencyProperty IsUpdatedOnEnterProperty = DependencyProperty.RegisterAttached("IsUpdatedOnEnter",
        typeof(bool), typeof(BindingSourceUpdateBehavior), new FrameworkPropertyMetadata(false, OnIsUpdatedOnEnterPropertyValueChanged));

    #endregion // Dependency Properties

    /////////////////////////////////////////////////////////////////////////////////////////////////////
    #region NON-PUBLIC PROCEDURES
    /////////////////////////////////////////////////////////////////////////////////////////////////////

    /// <summary>
    /// Handles the <c>KeyDown</c> event of an attached <see cref="UIElement"/> control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="KeyEventArgs"/> instance containing the event data.</param>
    private static void OnElementKeyDown(object sender, KeyEventArgs e) {
        if (e.Key == Key.Enter) {
            UpdateBindingSource(sender as UIElement);
            e.Handled = true;
        }
    }

    /// <summary>
    /// Called when <see cref="IsUpdatedOnEnterProperty"/> is changed.
    /// </summary>
    /// <param name="d">The dependency object that was changed.</param>
    /// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
    private static void OnIsUpdatedOnEnterPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        UIElement element = d as UIElement;
        if (null == element)
            return;

        if ((bool)e.NewValue)
            element.KeyDown += OnElementKeyDown;
        else
            element.KeyDown -= OnElementKeyDown;
    }

    /// <summary>
    /// Updated the binding expression of the text property on the specifed <see cref="UIElement"/>.
    /// </summary>
    /// <param name="element">The element.</param>
    private static void UpdateBindingSource(UIElement element) {
        if (element == null)
            return;

        BindingExpression bindingExpression = null;

        var textBox = element as TextBox;
        if (textBox != null) {
            bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);
        }
        else {
            var comboBox = element as ComboBox;
            if (comboBox != null && comboBox.IsEditable)
                bindingExpression = comboBox.GetBindingExpression(ComboBox.TextProperty);
        }

        if (bindingExpression != null)
            bindingExpression.UpdateSource();
    }

    #endregion // NON-PUBLIC PROCEDURES

    /////////////////////////////////////////////////////////////////////////////////////////////////////
    #region PUBLIC PROCEDURES
    /////////////////////////////////////////////////////////////////////////////////////////////////////

    /// <summary>
    /// Gets the value of the <see cref="IsUpdatedOnEnterProperty"/> attached property for a specified object.
    /// </summary>
    /// <param name="obj">The object to which the attached property is retrieved.</param>
    /// <returns>
    /// <c>true</c> if the containing drop-down should be closed when the <c>Escape</c> key is pressed; otherwise <c>false</c>.
    /// </returns>
    [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
    public static bool GetIsUpdatedOnEnter(UIElement obj) {
        if (null == obj) throw new ArgumentNullException("obj");
        return (bool)obj.GetValue(IsUpdatedOnEnterProperty);
    }
    /// <summary>
    /// Sets the value of the <see cref="IsUpdatedOnEnterProperty"/> attached property to a specified object.
    /// </summary>
    /// <param name="obj">The object to which the attached property is written.</param>
    /// <param name="value">
    /// A value indicating whether the containing drop-down should be closed when the <c>Escape</c> key is pressed.
    /// </param>
    [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
    public static void SetIsUpdatedOnEnter(UIElement obj, bool value) {
        if (null == obj) throw new ArgumentNullException("obj");
        obj.SetValue(IsUpdatedOnEnterProperty, value);
    }

    #endregion // PUBLIC PROCEDURES
}
We then set IsUpdatedOnEnter to true when we define our TextBox and ComboBox controls.

As a workaround until that maintenance release, you can include the behavior above in your code and apply it to the PropertyGrid like so:
<propgrid:PropertyGrid>
    <propgrid:PropertyGrid.Resources>
        <Style TargetType="TextBox">
            <Setter Property="local:BindingSourceUpdateBehavior.IsUpdatedOnEnter" Value="True" />
        </Style>
        <Style TargetType="ComboBox">
            <Setter Property="local:BindingSourceUpdateBehavior.IsUpdatedOnEnter" Value="True" />
        </Style>
    </propgrid:PropertyGrid.Resources>
</propgrid:PropertyGrid>
That would have the same effect as our new code.


Actipro Software Support

Posted 8 years ago by RS
Avatar
Does this new behavior complete the edit too (so you can use Up/Down to navigate properties), or just update the binding?

RS

Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Richard,

Well, no :-) Looking at the WinForms PropertyGrid, it does move the focus back to the name cell so you can easily navigate up/down using the arrow keys. Instead of the above approach, we've gone ahead and duplicated what the WinForms PropertyGrid does.

This new code is in the PropertyGridDataAccessorItem, so as long as the Enter key is not handled by the underlying controls it will work. So pressing Enter will effectively move focus to the item, and out of the underlying control (thus causing the binding to update the source). Once the item has focus, you can easily move up or down.


Actipro Software Support

Posted 8 years ago by RS
Avatar
Sounds good. This is the code we implemented a while back to handle Enter and Escape, and to fix focus when an edit isn't in progress. Would you new logic enable us to remove this do you think?
Public Sub New()
        [AddHandler](PreviewKeyDownEvent, New KeyEventHandler(AddressOf PreviewKeyDownHandler))
        [AddHandler](KeyDownEvent, New KeyEventHandler(AddressOf KeyDownHandler))
    End Sub

    Private Sub PreviewKeyDownHandler(ByVal sender As System.Object, ByVal e As System.Windows.Input.KeyEventArgs)
        If TypeOf e.OriginalSource Is TextBox AndAlso e.Key = Key.Return _
            AndAlso (e.KeyboardDevice.Modifiers And ConsoleModifiers.Control) <> ConsoleModifiers.Control Then

            ' Enter key in a TextBox - move focus back to the PropertyGridDataAccessorItem
            DirectCast(e.OriginalSource, UIElement).MoveFocus(New TraversalRequest(FocusNavigationDirection.Previous))
            e.Handled = True
        ElseIf TypeOf e.OriginalSource Is TextBox AndAlso e.Key = Key.Escape Then
            ' Cancel the edit in progress
            Dim TextProperty As DependencyProperty = Nothing
            TextProperty = TextBox.TextProperty

            Dim BindingExpression As BindingExpression = DirectCast(e.OriginalSource, FrameworkElement).GetBindingExpression(TextProperty)
            If BindingExpression IsNot Nothing Then
                BindingExpression.UpdateTarget()
                ' Now move focus back to the PropertyGridDataAccessorItem
                DirectCast(e.OriginalSource, UIElement).MoveFocus(New TraversalRequest(FocusNavigationDirection.Previous))
            End If
        End If
    End Sub

    Private Sub KeyDownHandler(ByVal sender As System.Object, ByVal e As System.Windows.Input.KeyEventArgs)
        If TypeOf e.OriginalSource Is PropertyGridDataAccessorItem Then
            ' This is the focus item when the property has focus but an edit isn't in progress
            DirectCast(e.OriginalSource, PropertyGridDataAccessorItem).MoveFocus(New TraversalRequest(FocusNavigationDirection.Next))
        End If
    End Sub

RS

Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Richard,

So there are really three cases here:

1. Move focus to the editor if the PropertyGridDataAccessorItem has focus and the user starts typing

Support for this is included in the latest release through the PropertyGrid.TextInputFocusDelegates property. This effectively maps a type (i.e. typeof(TextBox) or typeof(ToggleButton)) to an action that should be performed. When PropertyGridDataAccessorItem receives a PreviewTextInput event (from the user typing) then the next focusable control is obtained and the associated action is performed.

This gives you better control over what happens when the user types and the PropertyGridDataAccessorItem has focus. For example, the default action for ToggleButton (which includes CheckBox) is to toggle the value when the space is pressed. So you don't have to press space twice to toggle a check box.

It also allows you to add any custom types, which may need special support.

2. The Enter key should move focus back to the PropertyGridDataAccessorItem (thus committing any changes)

We've added support for this to the next maintenance release. Unlike your code, we perform this in the KeyDown event, not the PreviewKeyDown. We still need to be able to allow the underlying editor to handle the Enter key. But we will ignore Control+Enter, like you do above.

3. The Escape key should move focus back to the PropertyGridDataAccessorItem and cancel any changes

We've added support for this as well, but it's not as straight forward as the Enter key. Here we need to be able to "cancel" changes based on the control that has focus. So TextBox would work like you have shown, but the controls from our Editors for WPF product would need to perform a different action.

To accommodate this, we've added a PropertyGrid.EscapeKeyDownDelegates property which works like the TextInputFocusDelegates property. If the Escape key down event is received by the PropertyGridDataAccessorItem, then it will get the control with focus and try to find an associated delegate to execute. We have pre-defined delegates for TextBox and ComboBox, and if you use our Editors/PropertyGrid Interop there are default delegates for those controls.

So basically, this will cancel changes for the default controls, but still allow customization as needed. But again, this is handled in the KeyDown event, not the PreviewKeyDown. But otherwise, these should easily replace what you have.


Actipro Software Support

Posted 8 years ago by RS
Avatar
Awesome - look forward to removing our ugly hacks :) I wonder if we specifically wanted to prevent enter from being forwarded to our text boxes (to avoid adding new lines when confirming an edit) - I'll have to check that out.

RS

Posted 8 years ago by Craig - Varigence, Inc.
Avatar
I've downloaded the 545 build but the committing edit behavior doesn't seem to do anything. Is there any visual change that I can expect, to know that the edit has been committed, or do I need to implement that myself?

Thanks,

-Craig
Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Craig,

This feature hasn't been release yet, it will be included in the 2011.2 major release. The 0545 build only addressed issues in the 0544 build.


Actipro Software Support

The latest build of this product (v2018.1 build 0675) was released 13 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.